gloomberb 0.18.4 → 0.19.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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +169 -5
- package/src/account/auth-model.ts +7 -3
- package/src/api-client/auth.ts +9 -4
- package/src/api-client/catalysts.ts +2 -1
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +31 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +171 -11
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +13 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/request.ts +222 -41
- package/src/api-client/research-activity.ts +6 -12
- package/src/api-client/types.ts +74 -0
- package/src/app/keybindings/index.ts +3 -0
- package/src/app/keybindings/labels.ts +18 -1
- package/src/app/keybindings/resolve.ts +14 -0
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +42 -27
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +1 -1
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/fred-series.ts +5 -0
- package/src/cli/commands/keybindings.ts +23 -0
- package/src/cli/commands/market.ts +138 -46
- package/src/cli/commands/options-chain.ts +107 -10
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +176 -39
- package/src/cli/commands/search.ts +39 -3
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +57 -35
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +10 -2
- package/src/cli/export-source.ts +29 -1
- package/src/cli/glossary.ts +73 -1
- package/src/cli/history-rows.ts +87 -16
- package/src/cli/index.ts +2 -0
- package/src/cli/known-gaps.ts +5 -2
- package/src/cli/listing-arg.ts +195 -56
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +111 -16
- package/src/cli/pane-functions/function-help.ts +23 -13
- package/src/cli/pane-functions/headless.ts +56 -12
- package/src/cli/pane-functions/index.ts +58 -16
- package/src/cli/pane-functions/market-session.ts +81 -9
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/report.ts +5 -1
- package/src/cli/pane-functions/resolver.ts +35 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +30 -13
- package/src/cli/report-tables.ts +18 -7
- package/src/cli/restore-plugins.ts +13 -1
- package/src/cli/result.ts +57 -5
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +26 -4
- package/src/components/chart/composite/format.ts +27 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/levels.ts +2 -0
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +61 -5
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +46 -7
- package/src/components/chart/composite/series-axis-tags.tsx +16 -9
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/panel/prompt-binding.ts +0 -8
- package/src/components/command-bar/workflow/alert.ts +1 -1
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/header.tsx +26 -40
- package/src/components/layout/pane/footer/index.tsx +1 -0
- package/src/components/layout/pane/footer/model.ts +27 -0
- package/src/components/onboarding/onboarding-frame.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +4 -1
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/core/state/app/layout.ts +2 -0
- package/src/core/state/app/state.ts +9 -0
- package/src/core/state/app/types.ts +3 -0
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/saved-copies.ts +55 -16
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +427 -216
- package/src/i18n/es.ts +19 -3
- package/src/i18n/ja.ts +19 -3
- package/src/i18n/ko.ts +19 -3
- package/src/i18n/zh-cn.ts +19 -3
- package/src/i18n/zh-tw.ts +19 -3
- package/src/market-data/coordinator/financials.ts +10 -2
- package/src/market-data/coordinator/index.ts +3 -1
- package/src/market-data/coordinator/quotes.ts +4 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +48 -7
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/status.ts +16 -4
- package/src/market-data/options-alternatives.ts +141 -4
- package/src/market-data/quotes/contributions.ts +20 -2
- package/src/market-data/quotes/resolution.ts +3 -0
- package/src/market-data/selectors.ts +3 -0
- package/src/news/aggregator.ts +113 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/notification-log.ts +6 -4
- package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +380 -82
- package/src/plugins/builtin/alerts/command.ts +31 -0
- package/src/plugins/builtin/alerts/constants.ts +1 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- package/src/plugins/builtin/alerts/fired-log.ts +113 -8
- package/src/plugins/builtin/alerts/format.ts +80 -4
- package/src/plugins/builtin/alerts/headless.ts +30 -7
- package/src/plugins/builtin/alerts/history-rows.ts +7 -3
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +135 -22
- package/src/plugins/builtin/alerts/live.ts +3 -3
- package/src/plugins/builtin/alerts/metric-command.ts +134 -0
- package/src/plugins/builtin/alerts/metric-data.ts +111 -0
- package/src/plugins/builtin/alerts/metric-model.ts +324 -0
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
- package/src/plugins/builtin/alerts/pane.tsx +90 -39
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +37 -10
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/backtest/headless.ts +10 -5
- package/src/plugins/builtin/backtest/pane.tsx +9 -4
- package/src/plugins/builtin/backtest/presets.ts +29 -3
- package/src/plugins/builtin/broker-manager/index.tsx +10 -5
- package/src/plugins/builtin/broker-manager/model.ts +6 -1
- package/src/plugins/builtin/broker-trading/controller.ts +22 -12
- package/src/plugins/builtin/broker-trading/index.tsx +13 -1
- package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
- package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
- package/src/plugins/builtin/catalysts/model.ts +2 -1
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
- package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
- package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
- package/src/plugins/builtin/chart-composer/formula.ts +71 -18
- package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
- package/src/plugins/builtin/chart-composer/headless.ts +19 -3
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
- package/src/plugins/builtin/chart-composer/presets.ts +50 -15
- package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
- package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
- package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
- package/src/plugins/builtin/chat/content/composer.tsx +13 -6
- package/src/plugins/builtin/chat/content/footer.tsx +6 -3
- package/src/plugins/builtin/chat/content/index.tsx +50 -21
- package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
- package/src/plugins/builtin/chat/index.tsx +23 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
- package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
- package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -1
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +7 -7
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/calendar-model.ts +14 -9
- package/src/plugins/builtin/econ/figures.ts +72 -0
- package/src/plugins/builtin/econ/index.tsx +33 -3
- package/src/plugins/builtin/econ/types.ts +4 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
- package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
- package/src/plugins/builtin/econ-statistics/view.ts +3 -0
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
- package/src/plugins/builtin/exposure/charts.tsx +2 -2
- package/src/plugins/builtin/exposure/model.ts +13 -1
- package/src/plugins/builtin/exposure/pane.tsx +4 -4
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/futures/model.ts +2 -1
- package/src/plugins/builtin/futures/table.tsx +2 -2
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +11 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
- package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
- package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +36 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/pane.tsx +7 -5
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +25 -7
- package/src/plugins/builtin/market-movers/footer.ts +2 -2
- package/src/plugins/builtin/market-movers/headless.ts +32 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +14 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
- package/src/plugins/builtin/members/client.ts +19 -4
- package/src/plugins/builtin/members/headless.ts +20 -7
- package/src/plugins/builtin/members/index.tsx +9 -9
- package/src/plugins/builtin/members/model.ts +23 -4
- package/src/plugins/builtin/members/pane.tsx +25 -15
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +54 -6
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/model-spot.ts +5 -2
- package/src/plugins/builtin/options/strike-window.ts +2 -1
- package/src/plugins/builtin/options/table.ts +6 -9
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +42 -12
- package/src/plugins/builtin/options-positioning/client.ts +30 -0
- package/src/plugins/builtin/options-positioning/headless.ts +74 -21
- package/src/plugins/builtin/options-positioning/index.tsx +4 -4
- package/src/plugins/builtin/options-positioning/model.ts +122 -14
- package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/freshness.ts +12 -10
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/live-rows.ts +10 -3
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +32 -31
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
- package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/rate-path/pane.tsx +2 -2
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/scanner/feed.ts +2 -2
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +40 -6
- package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
- package/src/plugins/builtin/scanner/live-feed.ts +168 -0
- package/src/plugins/builtin/scanner/live-headless.ts +138 -0
- package/src/plugins/builtin/scanner/live-model.ts +362 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
- package/src/plugins/builtin/scanner/live-track.ts +186 -0
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +2 -1
- package/src/plugins/builtin/series-inventory.ts +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/macro-releases.ts +65 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +3 -2
- package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/headless.ts +6 -4
- package/src/plugins/builtin/themes/model.ts +17 -8
- package/src/plugins/builtin/themes/pane.tsx +7 -7
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
- package/src/plugins/builtin/vol-surface/client.ts +9 -17
- package/src/plugins/builtin/vol-surface/headless.ts +63 -14
- package/src/plugins/builtin/vol-surface/index.tsx +2 -2
- package/src/plugins/builtin/vol-surface/model.ts +22 -7
- package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
- package/src/plugins/builtin/vol-surface/tables.ts +31 -3
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +4 -4
- package/src/plugins/builtin/volatility/series.ts +26 -0
- package/src/plugins/builtin/world-indices/footer.ts +1 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-browser.ts +13 -8
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +5 -5
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/remote/resources.ts +2 -1
- package/src/renderers/browser/app-services.ts +1 -7
- package/src/renderers/browser/config-host.ts +2 -55
- package/src/renderers/browser/main.tsx +7 -57
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
- package/src/renderers/electrobun/bun/index.ts +25 -1
- package/src/renderers/electrobun/shared/app-name.ts +30 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/renderers/opentui/toast-host.tsx +71 -43
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
- package/src/sources/provider-router/batches.ts +10 -2
- package/src/sources/provider-router/financial-routes.ts +3 -3
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/history.ts +28 -7
- package/src/sources/provider-router/index.ts +149 -10
- package/src/sources/provider-router/search.ts +13 -2
- package/src/state/app/context/index.tsx +3 -0
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/auth-transition.ts +19 -9
- package/src/sync/baseline.ts +10 -4
- package/src/sync/controller.ts +70 -19
- package/src/sync/core-contributors.ts +90 -31
- package/src/sync/react.ts +4 -1
- package/src/sync/types.ts +3 -0
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/telemetry/usage-counts.ts +0 -18
- package/src/test-support/preload.ts +14 -0
- package/src/theme/colors.ts +8 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name.ts +103 -3
- package/src/tickers/listing-venues.ts +38 -3
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/index.ts +36 -7
- package/src/tickers/search/ranking.ts +10 -1
- package/src/tickers/search/types.ts +5 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/base-series.ts +13 -5
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +2 -4
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/forward-valuation.ts +32 -0
- package/src/time-series/fundamentals.ts +198 -7
- package/src/time-series/hooks.ts +1 -0
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/price-units.ts +72 -2
- package/src/time-series/resolve-context.ts +7 -1
- package/src/time-series/resolve.ts +116 -15
- package/src/time-series/series-presentation.ts +40 -6
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +5 -3
- package/src/time-series/studies.ts +278 -74
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +3 -3
- package/src/time-series/types.ts +27 -1
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/data-provider.ts +9 -1
- package/src/types/desktop-window.ts +2 -0
- package/src/types/financials.ts +73 -2
- package/src/types/headless.ts +61 -5
- package/src/types/instrument.ts +3 -0
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +10 -1
- package/src/ui/host.tsx +3 -0
- package/src/updater/command-names.ts +12 -7
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +44 -0
- package/src/utils/financial-statements.ts +6 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/network-failure.ts +9 -6
- package/src/utils/option-expiry.ts +48 -1
- package/src/utils/options.ts +10 -0
- package/src/utils/plugin-proxy-hosts.json +1 -0
- package/src/utils/price-history.ts +12 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
- package/src/renderers/browser/first-screen.ts +0 -193
|
@@ -16,12 +16,12 @@ import { TIME_RANGES, type TimeRange } from "../../../../time-series/range";
|
|
|
16
16
|
import type { PaneProps } from "../../../../types/plugin";
|
|
17
17
|
import type { PricePoint } from "../../../../types/financials";
|
|
18
18
|
import { colors, priceColor } from "../../../../theme/colors";
|
|
19
|
-
import { formatCompact, formatPercent } from "../../../../utils/format";
|
|
19
|
+
import { displayWidth, formatCompact, formatPercent } from "../../../../utils/format";
|
|
20
20
|
import { isPlainKeyboardEvent } from "../../../../utils/keyboard";
|
|
21
21
|
import { publicTickerKey } from "../../../../utils/exchanges";
|
|
22
|
-
import { quoteFormatOptions } from "../../../../market-data/market/format";
|
|
22
|
+
import { isPenceQuotedLine, penceFigure, quoteFormatOptions } from "../../../../market-data/market/format";
|
|
23
23
|
import {
|
|
24
|
-
barPriceDecimals, formatBarPrice, historyAllStart, historyBarsAfterHole, historyChangeBaseNote, historyMissingPeriodsNote,
|
|
24
|
+
barPriceDecimals, formatBarPrice, historyAllStart, historyBarsAfterHole, historyChangeBaseNote, historyMissingPeriodsNote, quotedPriceDivisor,
|
|
25
25
|
} from "../../../../cli/history-rows";
|
|
26
26
|
import {
|
|
27
27
|
MISSING_VOLUME_TEXT, missingVolumeCount, missingVolumeNotice, pricePointValues, priceHistoryIntegrityNotice,
|
|
@@ -89,8 +89,8 @@ function formatMaybePrice(value: number | null | undefined, decimals: number, wi
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/** The decimals the table's prices need: the one rule `history` and `fn HP` print by (barPriceDecimals). */
|
|
92
|
-
function historicalPriceDecimals(rows: readonly HistoricalPriceRow[], assetCategory?: string): number {
|
|
93
|
-
return barPriceDecimals(rows.map(({ point }) => point), assetCategory);
|
|
92
|
+
function historicalPriceDecimals(rows: readonly HistoricalPriceRow[], assetCategory?: string, priceDivisor = 1): number {
|
|
93
|
+
return barPriceDecimals(rows.map(({ point }) => point), assetCategory, priceDivisor);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/** The exported price: the digits the table shows, without the width limit. */
|
|
@@ -178,19 +178,27 @@ const HISTORY_NUMERIC_IDS = ["open", "high", "low", "close", "change", "changePe
|
|
|
178
178
|
/**
|
|
179
179
|
* Fixed date and volume columns; the price columns take the rest of the pane
|
|
180
180
|
* evenly, and give up cells down to a floor when it is narrow, so the volume
|
|
181
|
-
* stays on screen.
|
|
181
|
+
* stays on screen. A close with a note beside it (a London line's pence) asks
|
|
182
|
+
* for `closeWidth`, the widest close and note: the other columns give way to
|
|
183
|
+
* it first, down to their minimum, before it gives up its note.
|
|
182
184
|
*/
|
|
183
|
-
function buildHistoryColumns(width: number, intraday: boolean, size: HistoryBarSize, newestDate: string): HistoryColumn[] {
|
|
185
|
+
function buildHistoryColumns(width: number, intraday: boolean, size: HistoryBarSize, newestDate: string, closeWidth = 0): HistoryColumn[] {
|
|
184
186
|
// A month reads as `2026-10`; the newest bar, when its week or month runs on, adds `MTD` or `WTD`.
|
|
185
187
|
const dateWidth = Math.max(intraday ? 16 : size === "1mo" ? 7 : 10, newestDate.length);
|
|
186
|
-
const numeric: number[] = HISTORY_NUMERIC_IDS.map((id) => id === "changePercent" ? HISTORY_PERCENT_WIDTH
|
|
188
|
+
const numeric: number[] = HISTORY_NUMERIC_IDS.map((id) => id === "changePercent" ? HISTORY_PERCENT_WIDTH
|
|
189
|
+
: id === "close" ? Math.max(HISTORY_PRICE_WIDTH, closeWidth) : HISTORY_PRICE_WIDTH);
|
|
187
190
|
// One gap per column plus the table's inset on both sides.
|
|
188
191
|
const available = width - dateWidth - HISTORY_VOLUME_WIDTH - 8 - 2;
|
|
189
192
|
let spare = available - numeric.reduce((sum, value) => sum + value, 0);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
// The close keeps the width its note asks for while another column can give way, then gives it up too.
|
|
194
|
+
const closeAt = HISTORY_NUMERIC_IDS.indexOf("close");
|
|
195
|
+
for (const closeFloor of [Math.max(HISTORY_NUMERIC_MIN_WIDTH, closeWidth), HISTORY_NUMERIC_MIN_WIDTH]) {
|
|
196
|
+
const floor = (index: number) => index === closeAt ? closeFloor : HISTORY_NUMERIC_MIN_WIDTH;
|
|
197
|
+
for (let index = 0; spare < 0 && numeric.some((value, at) => value > floor(at)); index = (index + 1) % numeric.length) {
|
|
198
|
+
if (numeric[index]! > floor(index)) {
|
|
199
|
+
numeric[index]! -= 1;
|
|
200
|
+
spare += 1;
|
|
201
|
+
}
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
if (spare > 0) {
|
|
@@ -256,13 +264,25 @@ export function HistoricalPricesPane({ focused, width, height }: PaneProps) {
|
|
|
256
264
|
// Daily and longer bars are dates; only intraday bars carry a time.
|
|
257
265
|
const intraday = rows.length > 0 ? rows.some((row) => row.timed) : range === "1D" || range === "1W";
|
|
258
266
|
const newestRowDate = rows[0]?.date ?? "";
|
|
259
|
-
const columns = useMemo(() => buildHistoryColumns(width, intraday, size, newestRowDate), [intraday, newestRowDate, size, width]);
|
|
260
267
|
const assetCategory = quoteFormatOptions(
|
|
261
268
|
financials?.quote,
|
|
262
269
|
ticker?.metadata.assetCategory,
|
|
263
270
|
financials?.quoteMetadata?.instrumentType,
|
|
264
271
|
).assetCategory;
|
|
265
|
-
const
|
|
272
|
+
const priceDivisor = quotedPriceDivisor(quote);
|
|
273
|
+
const priceDecimals = useMemo(() => historicalPriceDecimals(rows, assetCategory, priceDivisor), [assetCategory, priceDivisor, rows]);
|
|
274
|
+
// A London line's close reads in pence too (6.385 638.5p): the close column asks for room for the widest.
|
|
275
|
+
const pence = isPenceQuotedLine(quote);
|
|
276
|
+
const closeWidth = useMemo(() => !pence ? 0 : rows.reduce((widest, row) => {
|
|
277
|
+
const close = row.point.close;
|
|
278
|
+
const pence = penceFigure(exportPrice(close, priceDecimals), quote, priceDecimals);
|
|
279
|
+
// A cell more than the text, so the close never runs into the low before it.
|
|
280
|
+
return pence ? Math.max(widest, displayWidth(` ${formatMaybePrice(close, priceDecimals, Infinity)} ${pence}`)) : widest;
|
|
281
|
+
}, 0), [pence, priceDecimals, quote, rows]);
|
|
282
|
+
const columns = useMemo(
|
|
283
|
+
() => buildHistoryColumns(width, intraday, size, newestRowDate, closeWidth),
|
|
284
|
+
[closeWidth, intraday, newestRowDate, size, width],
|
|
285
|
+
);
|
|
266
286
|
const boundedSelectedIdx = rows.length > 0 ? Math.min(selectedIdx, rows.length - 1) : -1;
|
|
267
287
|
const cycleRange = useCallback(() => setRange((current) => nextHistoryRange(current)), [setRange]);
|
|
268
288
|
|
|
@@ -296,12 +316,17 @@ export function HistoricalPricesPane({ focused, width, height }: PaneProps) {
|
|
|
296
316
|
case "open":
|
|
297
317
|
case "high":
|
|
298
318
|
case "low":
|
|
299
|
-
case "close":
|
|
319
|
+
case "close": {
|
|
320
|
+
const value = row.point[column.id];
|
|
321
|
+
// A London line's close also reads in the pence it trades in, where the column has room: 6.385 638.5p.
|
|
322
|
+
const pence = column.id === "close" ? penceFigure(exportPrice(value, priceDecimals), quote, priceDecimals) : null;
|
|
300
323
|
return {
|
|
301
|
-
text: formatMaybePrice(
|
|
302
|
-
value: exportPrice(
|
|
324
|
+
text: formatMaybePrice(value, priceDecimals, column.width),
|
|
325
|
+
value: exportPrice(value, priceDecimals),
|
|
326
|
+
...(pence ? { note: pence } : {}),
|
|
303
327
|
...(column.id === "close" ? { color: colors.textBright, attributes: TextAttributes.BOLD } : { color: colors.text }),
|
|
304
328
|
};
|
|
329
|
+
}
|
|
305
330
|
case "change":
|
|
306
331
|
return { text: spanMark(formatMaybePrice(row.change, priceDecimals, column.width - (row.changeSpansGap ? 1 : 0)), row, row.change),
|
|
307
332
|
value: exportPrice(row.change, priceDecimals), color: priceColor(row.change ?? 0) };
|
|
@@ -311,7 +336,7 @@ export function HistoricalPricesPane({ focused, width, height }: PaneProps) {
|
|
|
311
336
|
case "volume":
|
|
312
337
|
return { text: formatVolume(row.point.volume, volumeMissing > 0), value: row.point.volume ?? null, color: colors.textDim };
|
|
313
338
|
}
|
|
314
|
-
}, [priceDecimals, volumeMissing]);
|
|
339
|
+
}, [priceDecimals, quote, volumeMissing]);
|
|
315
340
|
|
|
316
341
|
// ALL starts where the source's bars do, when that is well inside the window (BTC-USD's in 2014-09).
|
|
317
342
|
const oldestDate = rows.at(-1)?.exportDate;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { fetchHistoryResult, InvalidHistoryResultError } from "../../../../sources/history-result";
|
|
2
3
|
import { historyRetentionNotice, isHistoryRetentionError } from "../../../../sources/history-retention";
|
|
3
4
|
import {
|
|
@@ -74,8 +75,8 @@ async function sessionVenue(provider: DataProvider, symbol: string, exchange: st
|
|
|
74
75
|
? await provider.getQuoteMetadata(symbol, exchange)
|
|
75
76
|
: await provider.getQuote(symbol, exchange);
|
|
76
77
|
return metadata?.listingExchangeName ?? "";
|
|
77
|
-
} catch {
|
|
78
|
-
return "";
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return logFailure("ticker-detail.data-panes.returns-history", "Read return session venue failed", "")(error);
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
import { isRoundTheClockCoin } from "../../../../utils/crypto-pair";
|
|
18
18
|
import { currentFxClosureStart, isCurrencyPairListing } from "../../../../utils/fx-market-hours";
|
|
19
19
|
import { pricePointValues } from "../../../../utils/price-history-integrity";
|
|
20
|
+
import { timeZoneCity, zonedClock } from "../../../../utils/utc-time";
|
|
20
21
|
import { zonedDateKey } from "../../../../utils/zoned-date-time";
|
|
22
|
+
import { historyDateHeader } from "./history-bars";
|
|
21
23
|
|
|
22
24
|
export type ReturnRange = "6H" | "1D" | "5D" | TimeRange;
|
|
23
25
|
|
|
@@ -347,6 +349,66 @@ export function returnBaseCloseDay(
|
|
|
347
349
|
return period ? barLastSession(day, period, venue, now) ?? barCloseDay(day, resolution, venue) : day;
|
|
348
350
|
}
|
|
349
351
|
|
|
352
|
+
/**
|
|
353
|
+
* The clock an intraday bar's time is shown on: its venue's own, where the app
|
|
354
|
+
* knows its sessions, so the open reads 09:30 and not 13:30; null for UTC, which
|
|
355
|
+
* is where a coin, a currency pair or a venue with no known zone keeps its bars.
|
|
356
|
+
*/
|
|
357
|
+
function returnClockZone(venue: string | undefined): string | null {
|
|
358
|
+
if (!venue || isAlwaysOpenExchange(venue)) return null;
|
|
359
|
+
return sessionCalendarTimeZone(venue);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* A bar as the table dates it: an intraday bar's open on its venue's clock
|
|
364
|
+
* (`2026-10-23 09:30`), a daily or longer bar its session day.
|
|
365
|
+
*/
|
|
366
|
+
export function returnTimeText(timestamp: number, resolution: ManualChartResolution, venue: string | undefined): string {
|
|
367
|
+
if (!isIntradayResolution(resolution)) return barSessionDay(timestamp, venue);
|
|
368
|
+
const zone = returnClockZone(venue);
|
|
369
|
+
if (!zone) return new Date(timestamp).toISOString().slice(0, 16).replace("T", " ");
|
|
370
|
+
const clock = zonedClock(timestamp, zone);
|
|
371
|
+
return `${clock.date} ${clock.time}`;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* What the time column is headed: the clock its times are on (`ET` for New York,
|
|
376
|
+
* as TAS heads it, the venue's city for another zone, else `UTC`), or what a
|
|
377
|
+
* daily or longer bar is dated by.
|
|
378
|
+
*/
|
|
379
|
+
export function returnTimeHeader(resolution: ManualChartResolution, venue: string | undefined): string {
|
|
380
|
+
if (!isIntradayResolution(resolution)) return historyDateHeader(resolution)?.toUpperCase() ?? "DATE";
|
|
381
|
+
const zone = returnClockZone(venue);
|
|
382
|
+
return `TIME ${!zone ? "UTC" : zone === "America/New_York" ? "ET" : timeZoneCity(zone).toUpperCase()}`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** The figures a window's rows add up to: its total, and the bars that moved most each way. */
|
|
386
|
+
export interface ReturnSummary {
|
|
387
|
+
/** The newest row's total return, a fraction. Null when no bar has one. */
|
|
388
|
+
total: number | null;
|
|
389
|
+
best: ReturnRow | null;
|
|
390
|
+
worst: ReturnRow | null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* The window's total return (the newest priced row's, measured from the base
|
|
395
|
+
* close) with its best and worst interval. A single bar has no interval worth
|
|
396
|
+
* ranking, and a window whose every interval is the same move has no best or
|
|
397
|
+
* worst, so neither is named then.
|
|
398
|
+
*/
|
|
399
|
+
export function summarizeReturns(rows: readonly ReturnRow[]): ReturnSummary {
|
|
400
|
+
// Rows are newest first.
|
|
401
|
+
const total = rows.find((row) => row.cumulativePercent !== null)?.cumulativePercent ?? null;
|
|
402
|
+
const moves = rows.filter((row) => row.intervalPercent !== null);
|
|
403
|
+
let best: ReturnRow | null = null;
|
|
404
|
+
let worst: ReturnRow | null = null;
|
|
405
|
+
for (const row of moves) {
|
|
406
|
+
if (!best || row.intervalPercent! > best.intervalPercent!) best = row;
|
|
407
|
+
if (!worst || row.intervalPercent! < worst.intervalPercent!) worst = row;
|
|
408
|
+
}
|
|
409
|
+
return best && worst && best.intervalPercent !== worst.intervalPercent ? { total, best, worst } : { total, best: null, worst: null };
|
|
410
|
+
}
|
|
411
|
+
|
|
350
412
|
/** Whether the newest daily row is the session still trading at its venue, which its table marks. */
|
|
351
413
|
export function isTodayReturnRow(row: Pick<ReturnRow, "timestamp"> | undefined, resolution: ManualChartResolution, venue: string | undefined, now: number): boolean {
|
|
352
414
|
return !!row && resolution === "1d" && isIntradayDailyBar(barSessionDay(row.timestamp, venue), venue, now);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
3
|
import { TextAttributes } from "../../../../ui";
|
|
3
4
|
import {
|
|
4
5
|
DataTableView,
|
|
5
6
|
QueryBar,
|
|
7
|
+
StatGrid,
|
|
6
8
|
loadingText,
|
|
7
9
|
unavailableText,
|
|
8
10
|
usePaneNoticeFooter,
|
|
@@ -12,11 +14,13 @@ import {
|
|
|
12
14
|
type DataTableColumn,
|
|
13
15
|
type PaneFooterSegment,
|
|
14
16
|
type PaneHint,
|
|
17
|
+
type StatItem,
|
|
15
18
|
} from "../../../../components";
|
|
16
19
|
import { usePaneRefreshKey } from "../../../../components/data-table/table-pane";
|
|
17
20
|
import { CHART_RESOLUTIONS } from "../../../../time-series/range";
|
|
18
21
|
import {
|
|
19
22
|
getChartResolutionLabel,
|
|
23
|
+
isIntradayResolution,
|
|
20
24
|
type ChartResolutionSupport,
|
|
21
25
|
type ManualChartResolution,
|
|
22
26
|
} from "../../../../time-series/resolution";
|
|
@@ -25,7 +29,7 @@ import { cleanFloat32Price, historyPriceDecimals } from "../../../../cli/history
|
|
|
25
29
|
import { formatPriceObservation, quoteFormatOptions } from "../../../../market-data/market/format";
|
|
26
30
|
import { usePaneTicker } from "../../../../state/app/context";
|
|
27
31
|
import type { PaneProps } from "../../../../types/plugin";
|
|
28
|
-
import { colors,
|
|
32
|
+
import { colors, shownPriceColor } from "../../../../theme/colors";
|
|
29
33
|
import { formatPercent } from "../../../../utils/format";
|
|
30
34
|
import { publicTickerKey } from "../../../../utils/exchanges";
|
|
31
35
|
import { priceHistoryIntegrityNotice } from "../../../../utils/price-history-integrity";
|
|
@@ -39,9 +43,12 @@ import {
|
|
|
39
43
|
effectiveReturnResolution,
|
|
40
44
|
isTodayReturnRow,
|
|
41
45
|
returnBaseCloseDay,
|
|
46
|
+
returnTimeHeader,
|
|
47
|
+
returnTimeText,
|
|
42
48
|
RETURN_RANGES,
|
|
43
49
|
type ReturnRange,
|
|
44
50
|
type ReturnRow,
|
|
51
|
+
summarizeReturns,
|
|
45
52
|
} from "./returns-model";
|
|
46
53
|
import { loadReturnSeries, type ReturnSeries } from "./returns-history";
|
|
47
54
|
import { formingHistoryBar, formingHistoryBarText, historyBarDate } from "./history-bars";
|
|
@@ -64,12 +71,6 @@ const COLUMNS: ReturnColumn[] = [
|
|
|
64
71
|
{ id: "cumulativePercent", label: "TOTAL %", width: 9, align: "right", flexGrow: 1 },
|
|
65
72
|
];
|
|
66
73
|
|
|
67
|
-
function formatDateTime(date: Date): string {
|
|
68
|
-
const iso = date.toISOString();
|
|
69
|
-
const hasTime = date.getUTCHours() !== 0 || date.getUTCMinutes() !== 0 || date.getUTCSeconds() !== 0;
|
|
70
|
-
return hasTime ? iso.slice(0, 16).replace("T", " ") : iso.slice(0, 10);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
74
|
/** Every price and dollar change at one decimal count, so the columns line up. */
|
|
74
75
|
function returnPriceDecimals(rows: readonly ReturnRow[], assetCategory?: string): number {
|
|
75
76
|
return historyPriceDecimals(rows.flatMap((row) => row.price === null ? [] : [{
|
|
@@ -95,6 +96,12 @@ function formatMaybePercent(value: number | null): string {
|
|
|
95
96
|
return value === null ? "-" : formatPercent(value);
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
/** A move's tone as it is printed: one that rounds to 0.00% is neutral, never red or green. */
|
|
100
|
+
function moveTone(fraction: number): StatItem["tone"] {
|
|
101
|
+
const shown = Number((fraction * 100).toFixed(2));
|
|
102
|
+
return shown > 0 ? "positive" : shown < 0 ? "negative" : "neutral";
|
|
103
|
+
}
|
|
104
|
+
|
|
98
105
|
function sortValue(row: ReturnRow, id: ReturnColumnId): number | null {
|
|
99
106
|
switch (id) {
|
|
100
107
|
case "time":
|
|
@@ -127,7 +134,7 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
127
134
|
symbol && dataProvider?.getChartResolutionSupport
|
|
128
135
|
? dataProvider.getChartResolutionSupport(symbol, exchange)
|
|
129
136
|
: NO_SUPPORT,
|
|
130
|
-
).catch((
|
|
137
|
+
).catch(logFailure("ticker-detail.data-panes.returns-pane", "Read return interval support failed", NO_SUPPORT)), [dataProvider, exchange, symbol]);
|
|
131
138
|
const [support, setSupport] = useState<readonly ChartResolutionSupport[] | null>(null);
|
|
132
139
|
useEffect(() => {
|
|
133
140
|
let cancelled = false;
|
|
@@ -162,15 +169,20 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
162
169
|
() => data ? buildReturnRows(data.points, data.visibleFrom, data.visibleTo) : [],
|
|
163
170
|
[data],
|
|
164
171
|
);
|
|
172
|
+
const barSize = data?.resolution ?? shownResolution;
|
|
173
|
+
const venue = data?.venue || undefined;
|
|
174
|
+
// The times are dated on the venue's own clock, which the column's header names.
|
|
175
|
+
const timeHeader = returnTimeHeader(barSize, venue);
|
|
176
|
+
const columns = useMemo(() => COLUMNS.map((column) => column.id === "time" ? { ...column, label: timeHeader } : column), [timeHeader]);
|
|
165
177
|
const table = useMemo(() => {
|
|
166
178
|
const searchable = returnRows.length >= SEARCH_MIN_ROWS;
|
|
167
179
|
const needle = searchable ? query.trim().toLowerCase() : "";
|
|
168
180
|
const rows = (needle
|
|
169
|
-
? returnRows.filter((row) =>
|
|
181
|
+
? returnRows.filter((row) => returnTimeText(row.timestamp, barSize, venue).includes(needle))
|
|
170
182
|
: returnRows.slice()
|
|
171
183
|
).sort((left, right) => compareSortValues(sortValue(left, sort.columnId), sortValue(right, sort.columnId), sort.direction));
|
|
172
184
|
return { rows, searchable };
|
|
173
|
-
}, [query, returnRows, sort]);
|
|
185
|
+
}, [barSize, query, returnRows, sort, venue]);
|
|
174
186
|
// The quote knows the instrument type even when the saved ticker does not,
|
|
175
187
|
// so prices take the same decimals as Historical Prices.
|
|
176
188
|
const { financials } = usePaneTicker();
|
|
@@ -199,7 +211,6 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
199
211
|
// Today's daily bar is the session still trading, marked as Historical Prices marks it.
|
|
200
212
|
const dayToDate = !!data && isTodayReturnRow(returnRows[0], data.resolution, data.venue || undefined, Date.now());
|
|
201
213
|
const formingKey = forming || dayToDate ? returnRows[0]?.key : undefined;
|
|
202
|
-
const barSize = data?.resolution ?? shownResolution;
|
|
203
214
|
// The close every total is measured from: `from Mon 21 Sep close`.
|
|
204
215
|
const baseText = useMemo(() => {
|
|
205
216
|
const day = data && returnRows.length ? returnBaseCloseDay(data.points, data.visibleFrom, data.resolution, data.venue || undefined, Date.now()) : null;
|
|
@@ -225,6 +236,29 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
225
236
|
title: "Price history data",
|
|
226
237
|
});
|
|
227
238
|
|
|
239
|
+
// The window's total and its best and worst interval, over every bar it holds, whatever the search narrows the table to.
|
|
240
|
+
const summary = useMemo<StatItem[]>(() => {
|
|
241
|
+
const { total, best, worst } = summarizeReturns(returnRows);
|
|
242
|
+
const moveItem = (id: string, label: string, row: ReturnRow | null): StatItem[] => {
|
|
243
|
+
if (row?.intervalPercent == null) return [];
|
|
244
|
+
const when = historyBarDate(returnTimeText(row.timestamp, barSize, venue), barSize, false);
|
|
245
|
+
return [{
|
|
246
|
+
id,
|
|
247
|
+
label,
|
|
248
|
+
value: formatPercent(row.intervalPercent),
|
|
249
|
+
tone: moveTone(row.intervalPercent),
|
|
250
|
+
detail: when,
|
|
251
|
+
// A narrow pane keeps the day without its year, so the three figures stay on one row.
|
|
252
|
+
...isIntradayResolution(barSize) ? { detailVariants: [when.slice(5)] } : {},
|
|
253
|
+
}];
|
|
254
|
+
};
|
|
255
|
+
return [
|
|
256
|
+
...total === null ? [] : [{ id: "total", label: "Total", value: formatPercent(total), tone: moveTone(total) }],
|
|
257
|
+
...moveItem("best", "Best", best),
|
|
258
|
+
...moveItem("worst", "Worst", worst),
|
|
259
|
+
];
|
|
260
|
+
}, [barSize, returnRows, venue]);
|
|
261
|
+
|
|
228
262
|
const chooseSort = useCallback((id: string) => {
|
|
229
263
|
setSort((current) => nextHeaderSort(current, id as ReturnColumnId, { firstDirection: "desc" }));
|
|
230
264
|
}, []);
|
|
@@ -232,10 +266,10 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
232
266
|
const renderCell = useCallback((row: ReturnRow, column: ReturnColumn): DataTableCell => {
|
|
233
267
|
switch (column.id) {
|
|
234
268
|
case "time": {
|
|
235
|
-
const shown =
|
|
269
|
+
const shown = returnTimeText(row.timestamp, barSize, venue);
|
|
236
270
|
// A daily or longer bar is a date; a month's reads as the month, and one still running says so.
|
|
237
271
|
return {
|
|
238
|
-
text:
|
|
272
|
+
text: isIntradayResolution(barSize) ? shown : historyBarDate(shown, barSize, row.key === formingKey),
|
|
239
273
|
value: new Date(row.timestamp).toISOString(),
|
|
240
274
|
color: colors.textDim,
|
|
241
275
|
};
|
|
@@ -243,15 +277,15 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
243
277
|
case "price":
|
|
244
278
|
return { text: formatMaybePrice(row.price, priceDecimals, column.width), value: exportPrice(row.price, priceDecimals), color: colors.textBright, attributes: TextAttributes.BOLD };
|
|
245
279
|
case "intervalChange":
|
|
246
|
-
return { text: formatMaybePrice(row.intervalChange, priceDecimals, column.width), value: exportPrice(row.intervalChange, priceDecimals), color:
|
|
280
|
+
return { text: formatMaybePrice(row.intervalChange, priceDecimals, column.width), value: exportPrice(row.intervalChange, priceDecimals), color: shownPriceColor(row.intervalChange ?? 0, priceDecimals) };
|
|
247
281
|
case "intervalPercent":
|
|
248
|
-
return { text: formatMaybePercent(row.intervalPercent), value: row.intervalPercent === null ? null : row.intervalPercent * 100, color:
|
|
282
|
+
return { text: formatMaybePercent(row.intervalPercent), value: row.intervalPercent === null ? null : row.intervalPercent * 100, color: shownPriceColor((row.intervalPercent ?? 0) * 100, 2) };
|
|
249
283
|
case "cumulativeChange":
|
|
250
|
-
return { text: formatMaybePrice(row.cumulativeChange, priceDecimals, column.width), value: exportPrice(row.cumulativeChange, priceDecimals), color:
|
|
284
|
+
return { text: formatMaybePrice(row.cumulativeChange, priceDecimals, column.width), value: exportPrice(row.cumulativeChange, priceDecimals), color: shownPriceColor(row.cumulativeChange ?? 0, priceDecimals) };
|
|
251
285
|
case "cumulativePercent":
|
|
252
|
-
return { text: formatMaybePercent(row.cumulativePercent), value: row.cumulativePercent === null ? null : row.cumulativePercent * 100, color:
|
|
286
|
+
return { text: formatMaybePercent(row.cumulativePercent), value: row.cumulativePercent === null ? null : row.cumulativePercent * 100, color: shownPriceColor((row.cumulativePercent ?? 0) * 100, 2) };
|
|
253
287
|
}
|
|
254
|
-
}, [barSize, formingKey, priceDecimals]);
|
|
288
|
+
}, [barSize, formingKey, priceDecimals, venue]);
|
|
255
289
|
|
|
256
290
|
return (
|
|
257
291
|
<DataTableView<ReturnRow, ReturnColumn>
|
|
@@ -259,7 +293,7 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
259
293
|
selection={{ kind: "none" }}
|
|
260
294
|
rootWidth={width}
|
|
261
295
|
rootHeight={height}
|
|
262
|
-
columns={
|
|
296
|
+
columns={columns}
|
|
263
297
|
items={table.rows}
|
|
264
298
|
sortColumnId={sort.columnId}
|
|
265
299
|
sortDirection={sort.direction}
|
|
@@ -268,33 +302,36 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
268
302
|
renderCell={renderCell}
|
|
269
303
|
selectedTextOverridesCellColor
|
|
270
304
|
rootBefore={(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
value
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
<>
|
|
306
|
+
<QueryBar
|
|
307
|
+
width={width}
|
|
308
|
+
search={table.searchable ? {
|
|
309
|
+
value: query,
|
|
310
|
+
onChange: setQuery,
|
|
311
|
+
placeholder: "date or time",
|
|
312
|
+
focused,
|
|
313
|
+
...searchProps,
|
|
314
|
+
} : undefined}
|
|
315
|
+
filters={[{
|
|
316
|
+
id: "range",
|
|
317
|
+
label: "Range",
|
|
318
|
+
value: range,
|
|
319
|
+
options: RETURN_RANGES.map((value) => ({ value, label: value })),
|
|
320
|
+
onChange: setRange,
|
|
321
|
+
}, {
|
|
322
|
+
id: "interval",
|
|
323
|
+
label: "Interval",
|
|
324
|
+
value: shownResolution,
|
|
325
|
+
options: INTERVAL_OPTIONS.map((value) => ({
|
|
326
|
+
value,
|
|
327
|
+
label: getChartResolutionLabel(value),
|
|
328
|
+
disabled: support !== null && !availableResolutions.has(value),
|
|
329
|
+
})),
|
|
330
|
+
onChange: setResolution,
|
|
331
|
+
}]}
|
|
332
|
+
/>
|
|
333
|
+
<StatGrid items={summary} width={width} />
|
|
334
|
+
</>
|
|
298
335
|
)}
|
|
299
336
|
getExportMetadata={() => [
|
|
300
337
|
["Ticker", symbol ? publicTickerKey(symbol, exchange) : ""],
|
|
@@ -4,7 +4,7 @@ import { EXTENDED_SESSION_LABELS, getQuoteSessionFields, quoteHeadlineTime, type
|
|
|
4
4
|
import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneFreshness, HeadlessPaneOptionValues } from "../../../types/headless";
|
|
5
5
|
import type { TimeRange } from "../../../time-series/range";
|
|
6
6
|
import { formatNumber, formatPercentRaw } from "../../../utils/format";
|
|
7
|
-
import { formatMarketPrice, formatMarketPriceWithCurrency, formatPriceObservation, quoteFormatOptions, withCurrencyMinorDigits, type MarketFormatOptions } from "../../../market-data/market/format";
|
|
7
|
+
import { formatMarketPrice, formatMarketPriceWithCurrency, formatPriceObservation, penceFigure, quoteFormatOptions, withCurrencyMinorDigits, type MarketFormatOptions } from "../../../market-data/market/format";
|
|
8
8
|
import {
|
|
9
9
|
MISSING_VOLUME_TEXT, missingVolumeCount, missingVolumeNotice, pricePointValues, priceHistoryIntegrityNotice,
|
|
10
10
|
} from "../../../utils/price-history-integrity";
|
|
@@ -36,7 +36,8 @@ import {
|
|
|
36
36
|
} from "../shared/headless-market-data";
|
|
37
37
|
import { barHistoryFreshness, quoteFreshnessFields, REPORTED_DATA } from "../shared/report-freshness";
|
|
38
38
|
import {
|
|
39
|
-
BAR_INTERVAL_LABELS, barPriceDecimals, cleanFloat32Price, formatBarPrice, historyAllStart, historyAllStartNote, historyMissingPeriodsNote, historyPriceDecimals,
|
|
39
|
+
BAR_INTERVAL_LABELS, barPriceDecimals, cleanBarPrice, cleanFloat32Price, formatBarPrice, historyAllStart, historyAllStartNote, historyMissingPeriodsNote, historyPriceDecimals,
|
|
40
|
+
quotedPriceDivisor,
|
|
40
41
|
} from "../../../cli/history-rows";
|
|
41
42
|
import { formatUtcTime } from "../../../utils/utc-time";
|
|
42
43
|
import { maxDrawdown, maxDrawdownText } from "../../../utils/max-drawdown";
|
|
@@ -197,7 +198,7 @@ async function loadRatioReport(
|
|
|
197
198
|
const history = oldest && tab.key === "valuation"
|
|
198
199
|
? await resolveHeadlessInstrument(ctx, symbol)
|
|
199
200
|
.then(({ symbol: bare, exchange }) => loadPeriodEndHistory(ctx.marketData, bare, exchange ?? "", oldest))
|
|
200
|
-
.catch((
|
|
201
|
+
.catch(logFailure("ticker-detail.headless", "Load valuation price history failed", null))
|
|
201
202
|
: null;
|
|
202
203
|
const table = hasRequestedStatements ? ratioTableForFinancials(financials, tab, period, history) : null;
|
|
203
204
|
const statementCurrency = financialStatementCurrency(financials, [
|
|
@@ -339,12 +340,16 @@ export const quoteComparisonHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
339
340
|
* report declares before it has bars keep every digit the source sent. A bar without
|
|
340
341
|
* volume in a history that reports it (`volumeMissing`) prints it as missing, never as 0.
|
|
341
342
|
*/
|
|
342
|
-
const historyPriceColumns = (volumeMissing: boolean, decimals?: number): HeadlessPaneColumn[] => ["open", "high", "low", "close", "volume"].map((key) => ({
|
|
343
|
+
const historyPriceColumns = (volumeMissing: boolean, decimals?: number, pence?: (close: number) => string | null): HeadlessPaneColumn[] => ["open", "high", "low", "close", "volume"].map((key) => ({
|
|
343
344
|
key, header: key, align: "right" as const,
|
|
344
|
-
format: (value: unknown) =>
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
345
|
+
format: (value: unknown) => {
|
|
346
|
+
if (value == null || !Number.isFinite(Number(value))) return key === "volume" && volumeMissing ? MISSING_VOLUME_TEXT : "-";
|
|
347
|
+
if (key === "volume") return formatNumber(Number(value), 0);
|
|
348
|
+
if (decimals == null) return formatPriceObservation(Number(value), { minimumFractionDigits: 2 });
|
|
349
|
+
// A London line's close also reads in pence, as the pane's close notes it: 6.385 638.5p.
|
|
350
|
+
const note = key === "close" ? pence?.(Number(value)) : null;
|
|
351
|
+
return `${formatBarPrice(Number(value), decimals)}${note ? ` ${note}` : ""}`;
|
|
352
|
+
},
|
|
348
353
|
}));
|
|
349
354
|
|
|
350
355
|
/**
|
|
@@ -353,7 +358,14 @@ const historyPriceColumns = (volumeMissing: boolean, decimals?: number): Headles
|
|
|
353
358
|
* whose period runs on (`2026-10 MTD`); the export keeps each bar's own date
|
|
354
359
|
* and adds a `Partial` column when one is.
|
|
355
360
|
*/
|
|
356
|
-
function historyColumns(
|
|
361
|
+
function historyColumns(
|
|
362
|
+
size: HistoryBarSize,
|
|
363
|
+
partial: boolean,
|
|
364
|
+
text: boolean,
|
|
365
|
+
volumeMissing = false,
|
|
366
|
+
decimals?: number,
|
|
367
|
+
pence?: (close: number) => string | null,
|
|
368
|
+
): HeadlessPaneColumn[] {
|
|
357
369
|
const daily = isDailyOrLongerBar(size);
|
|
358
370
|
const date: HeadlessPaneColumn = {
|
|
359
371
|
key: "date",
|
|
@@ -365,7 +377,7 @@ function historyColumns(size: HistoryBarSize, partial: boolean, text: boolean, v
|
|
|
365
377
|
const marked: HeadlessPaneColumn[] = partial && !text
|
|
366
378
|
? [{ key: "partial", header: "Partial", format: (value) => value === true ? "true" : "" }]
|
|
367
379
|
: [];
|
|
368
|
-
return [date, ...historyPriceColumns(volumeMissing, decimals), ...marked];
|
|
380
|
+
return [date, ...historyPriceColumns(volumeMissing, decimals, text ? pence : undefined), ...marked];
|
|
369
381
|
}
|
|
370
382
|
|
|
371
383
|
export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
@@ -386,14 +398,16 @@ export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
386
398
|
window ? loadPriceWindowHistory(ctx.marketData, listing, exchange ?? "", window).then((result) => result.points)
|
|
387
399
|
: loadHeadlessPriceHistory(ctx, symbol, range),
|
|
388
400
|
pastWindow || (!window && isIntradayResolution(RANGE_HISTORY_RESOLUTION[range])) ? null
|
|
389
|
-
: ctx.marketData.getQuote(listing, exchange ?? "").catch((
|
|
401
|
+
: ctx.marketData.getQuote(listing, exchange ?? "").catch(logFailure("ticker-detail.headless", "Load historical prices quote failed", null)),
|
|
390
402
|
]);
|
|
391
403
|
// The venue whose sessions say whether a day, week or month is still running: the key's, or for a bare symbol its quote's.
|
|
392
404
|
const venue = exchange || quote?.listingExchangeName || quote?.exchangeName || undefined;
|
|
393
405
|
const now = Date.now();
|
|
394
406
|
const points = withSessionBar(loaded, quote, { now, venue });
|
|
395
|
-
// Providers serve float32 prices (4013.39990234 for 4013.4): the rows hold the decimal each is, as `history` does
|
|
396
|
-
|
|
407
|
+
// Providers serve float32 prices (4013.39990234 for 4013.4): the rows hold the decimal each is, as `history` does,
|
|
408
|
+
// a London line's cleaned in the pence it was priced in.
|
|
409
|
+
const priceDivisor = quotedPriceDivisor(quote);
|
|
410
|
+
const clean = (value: number | null) => value == null ? null : cleanBarPrice(value, priceDivisor);
|
|
397
411
|
const rows = points.map((point) => {
|
|
398
412
|
const date = new Date(point.date);
|
|
399
413
|
const values = pricePointValues(point);
|
|
@@ -417,7 +431,7 @@ export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
417
431
|
&& quote.lastUpdated >= Date.parse(`${rows.at(-1)!.date.slice(0, 10)}T00:00:00Z`) ? new Date(quote.lastUpdated).toISOString() : null;
|
|
418
432
|
const shown = forming ? [...rows.slice(0, -1), { ...rows.at(-1)!, partial: true }] : rows;
|
|
419
433
|
// One decimal count for the table, by the instrument's display rules when the quote names its kind.
|
|
420
|
-
const decimals = barPriceDecimals(rows, quoteFormatOptions(quote).assetCategory);
|
|
434
|
+
const decimals = barPriceDecimals(rows, quoteFormatOptions(quote).assetCategory, priceDivisor);
|
|
421
435
|
const marked = shown.some((row) => row.partial === true);
|
|
422
436
|
const volumeMissing = missingVolumeCount(shown);
|
|
423
437
|
const volumeNote = missingVolumeNotice(volumeMissing, shown.length);
|
|
@@ -443,7 +457,8 @@ export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
443
457
|
return {
|
|
444
458
|
rows: shown,
|
|
445
459
|
columns: historyColumns(size, marked, false, false, decimals),
|
|
446
|
-
textColumns: historyColumns(size, marked, true, volumeMissing > 0, decimals
|
|
460
|
+
textColumns: historyColumns(size, marked, true, volumeMissing > 0, decimals,
|
|
461
|
+
(close) => penceFigure(Number(close.toFixed(Math.min(20, decimals))), quote, decimals)),
|
|
447
462
|
freshness,
|
|
448
463
|
...(notes.length > 0 ? { notes } : {}),
|
|
449
464
|
...(notice ? { complete: false } : {}),
|
|
@@ -474,7 +489,7 @@ export const returnsHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
474
489
|
// --from 2007 --to 2009 measures those dates' returns in place of the range.
|
|
475
490
|
const window = readReportWindow(options);
|
|
476
491
|
const pastWindow = !!window && isPastPriceWindow(window);
|
|
477
|
-
const support = await Promise.resolve(ctx.marketData.getChartResolutionSupport?.(symbol, exchange ?? "") ?? []).catch((
|
|
492
|
+
const support = await Promise.resolve(ctx.marketData.getChartResolutionSupport?.(symbol, exchange ?? "") ?? []).catch(logFailure("ticker-detail.headless", "Read return interval support failed", []));
|
|
478
493
|
const now = Date.now();
|
|
479
494
|
const series = await loadReturnSeries(ctx.marketData, {
|
|
480
495
|
symbol, exchange: exchange ?? "", range, resolution: requested, support, now, forceRefresh: ctx.refresh, window,
|
|
@@ -221,7 +221,7 @@ export function buildOverviewStats({
|
|
|
221
221
|
const count = ratioCount(pair.ratio);
|
|
222
222
|
stats.push(receiptSide
|
|
223
223
|
? { label: "ADR Ratio", value: `1:${count}`, detail: [adrRatioText(pair.ratio)!, `${count} sh per ADR`], link: ADR_PAIR }
|
|
224
|
-
: { label: "ADR", value: pair.receipt, detail: [`1 ADR = ${count} of these
|
|
224
|
+
: { label: "ADR", value: pair.receipt, detail: [`1 ADR = ${count} of these shares`, `1:${count}`], link: ADR_PAIR });
|
|
225
225
|
}
|
|
226
226
|
const trailingPE = liveTrailingPE(quote, fundamentals);
|
|
227
227
|
if (trailingPE != null) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
1
2
|
import { Box } from "../../../ui";
|
|
2
3
|
import { getCurrentPluginTarget } from "../../current-target";
|
|
3
4
|
import { recordResearchActivity, recordResearchTabView } from "../../../api-client/research-activity";
|
|
@@ -283,7 +284,7 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
|
|
|
283
284
|
choices={choices.map((item) => ({ ...item, label: t(item.label) }))} />,
|
|
284
285
|
}).then((templateId) => {
|
|
285
286
|
if (templateId && choices.some((item) => item.id === templateId)) createPaneFromTemplate(templateId, { symbol: companySymbol });
|
|
286
|
-
}).catch((
|
|
287
|
+
}).catch(logFailure("ticker-detail.pane", "Open company research choices failed"));
|
|
287
288
|
} }];
|
|
288
289
|
}, [companySymbol, createPaneFromTemplate, dialog, disabledPlugins, registry, tickerResearchTabsSnapshot]);
|
|
289
290
|
usePaneMenuItems("ticker-research:go-to-tab", () => {
|
|
@@ -304,7 +305,7 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
|
|
|
304
305
|
),
|
|
305
306
|
}).then((tabId) => {
|
|
306
307
|
if (tabId) selectResearchTab(tabId);
|
|
307
|
-
}).catch((
|
|
308
|
+
}).catch(logFailure("ticker-detail.pane", "Open research tab choices failed"));
|
|
308
309
|
},
|
|
309
310
|
}];
|
|
310
311
|
}, [dialog, resolvedTabId, selectResearchTab, showTabs, tabItems]);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { savedCopyFooterSegment, type PaneFooterSegment } from "../../../components/layout/pane/footer";
|
|
2
|
+
import { savedCopyAt } from "../../../data/saved-copies";
|
|
2
3
|
import { t } from "../../../i18n";
|
|
3
4
|
import { isQuoteStaleForCurrentSession, noPrintsTodaySince } from "../../../market-data/quotes/freshness";
|
|
4
5
|
import { formatQuoteNavAsOf } from "../../../market-data/quotes/time";
|
|
@@ -13,9 +14,13 @@ export function tickerQuoteFooterInfo(
|
|
|
13
14
|
): PaneFooterSegment[] {
|
|
14
15
|
const info: PaneFooterSegment[] = [];
|
|
15
16
|
const navAsOf = formatQuoteNavAsOf(quote);
|
|
17
|
+
const savedAt = savedCopyAt(quote);
|
|
16
18
|
if (quote?.delisting) {
|
|
17
19
|
// A listing that no longer trades: its quote is its last trade, said once with its date.
|
|
18
20
|
info.push({ id: "ticker-research-stale", parts: [{ text: delistingLabel(quote.delisting), tone: "warning" }] });
|
|
21
|
+
} else if (savedAt != null) {
|
|
22
|
+
// Offline: the quote is the copy saved when it last refreshed, and the footer says when that was.
|
|
23
|
+
info.push(savedCopyFooterSegment("ticker-research-saved", savedAt));
|
|
19
24
|
} else if (isQuoteStaleForCurrentSession(quote)) {
|
|
20
25
|
// In a session the calendar has open with nothing printed in it, the warning says since when.
|
|
21
26
|
const quiet = noPrintsTodaySince(quote) != null;
|