@gloom-sh/terminal 0.18.5 → 0.19.1
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 +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- 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/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -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 +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- 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/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/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- 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 +8 -21
- 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 +18 -3
- package/src/components/ui/icon.tsx +4 -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/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- 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 +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -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 +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -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/broker-manager/index.tsx +7 -3
- 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 +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- 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/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -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 +10 -10
- 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/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- 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/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- 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 +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- 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 +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -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 +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- 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 +33 -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/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- 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 +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -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 +22 -4
- 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 +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -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 +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- 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/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- 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 +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- 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/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- 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 +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- 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/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- 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 +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- 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 +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- 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/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -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 +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- 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 +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- 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 +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- 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-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- 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/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -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 +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- 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/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- 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/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- 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/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -2,7 +2,7 @@ import type { PaneTemplateInstanceConfig } from "../../../types/plugin";
|
|
|
2
2
|
import type { PluginModule } from "../plugin-module";
|
|
3
3
|
import { commandRequestParams } from "../shared/command-tab";
|
|
4
4
|
import { PERP_SERIES } from "./series";
|
|
5
|
-
import { perpsCache, perpsHistoryCache, perpsListCache, perpsMarketCache, perpsRankingsCache } from "./client";
|
|
5
|
+
import { perpsCache, perpsDepthCache, perpsHistoryCache, perpsLiquidationsCache, perpsListCache, perpsMarketCache, perpsRankingsCache } from "./client";
|
|
6
6
|
import { perpsHeadless } from "./headless";
|
|
7
7
|
import { PerpsPane } from "./pane";
|
|
8
8
|
|
|
@@ -18,16 +18,18 @@ export function perpsInstance(arg: string | undefined): PaneTemplateInstanceConf
|
|
|
18
18
|
settings: { market }, params: commandRequestParams({ market }) };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const PERP_CACHES = [perpsCache, perpsHistoryCache, perpsListCache, perpsMarketCache, perpsRankingsCache, perpsLiquidationsCache, perpsDepthCache] as const;
|
|
22
|
+
|
|
21
23
|
export const perpsModule: PluginModule = {
|
|
22
24
|
series: PERP_SERIES,
|
|
23
25
|
panes: [{ id: "perps", name: "Perpetuals", icon: "P", component: PerpsPane, defaultPosition: "right", defaultMode: "floating",
|
|
24
26
|
defaultFloatingSize: { width: 138, height: 34 }, tableExport: true, headless: perpsHeadless }],
|
|
25
|
-
paneTemplates: [{ id: "perps-pane", paneId: "perps", label: "Perpetuals", description: "Perpetual funding, open interest and premiums by market and venue, with rankings, a venue comparison and Pro history.",
|
|
26
|
-
keywords: ["perp", "perpetual", "perpetuals", "funding", "funding rate", "open interest", "hip-3", "stock perps", "basis", "basis trade", "carry", "venue", "funding spread", "long short ratio", "long/short", "positioning"],
|
|
27
|
+
paneTemplates: [{ id: "perps-pane", paneId: "perps", label: "Perpetuals", description: "Perpetual funding, open interest and premiums by market and venue, with rankings, a venue comparison, liquidations, order book depth and Pro history.",
|
|
28
|
+
keywords: ["perp", "perpetual", "perpetuals", "funding", "funding rate", "open interest", "hip-3", "stock perps", "basis", "basis trade", "carry", "venue", "funding spread", "long short ratio", "long/short", "positioning", "liquidations", "liquidation", "order book depth", "market depth", "liquidity"],
|
|
27
29
|
shortcut: { prefix: "PERP", argKind: "text", argOptional: true, argPlaceholder: "market" }, headless: perpsHeadless,
|
|
28
30
|
createInstance: (_ctx, options) => perpsInstance(options?.arg) }],
|
|
29
31
|
setup(ctx) {
|
|
30
|
-
for (const cache of
|
|
32
|
+
for (const cache of PERP_CACHES) cache.attach(ctx.persistence);
|
|
31
33
|
},
|
|
32
|
-
dispose() { for (const cache of
|
|
34
|
+
dispose() { for (const cache of PERP_CACHES) cache.reset(); },
|
|
33
35
|
};
|
|
@@ -6,14 +6,28 @@ import { formatCompact } from "../../../utils/format";
|
|
|
6
6
|
import { isUsListingExchange, publicTickerKey } from "../../../utils/exchanges";
|
|
7
7
|
import { isRecord } from "../../../utils/guards";
|
|
8
8
|
import type { ThemeColors } from "../../../theme/colors";
|
|
9
|
-
export const PERP_TABS = ["board", "rankings", "compare", "history", "evidence"] as const;
|
|
9
|
+
export const PERP_TABS = ["board", "rankings", "compare", "liquidations", "depth", "history", "evidence"] as const;
|
|
10
10
|
export type PerpTab = typeof PERP_TABS[number];
|
|
11
11
|
/** One market's tabs; the others list many markets. */
|
|
12
12
|
export const isMarketTab = (tab: string) => tab === "history" || tab === "evidence";
|
|
13
|
-
/**
|
|
14
|
-
export const
|
|
13
|
+
/** One base asset's tabs, across its contracts on every venue; they share the asset. */
|
|
14
|
+
export const isAssetTab = (tab: string) => tab === "compare" || tab === "liquidations" || tab === "depth";
|
|
15
|
+
/**
|
|
16
|
+
* A named market opens its history, or its latest values (Evidence) where history is Pro's, so a free account
|
|
17
|
+
* lands on the figures it has and not on a lock; nothing named opens the board.
|
|
18
|
+
*/
|
|
19
|
+
export const openingTab = (market: string | null | undefined, pro = true): PerpTab => market?.trim() ? pro ? "history" : "evidence" : "board";
|
|
15
20
|
export const ASSET_FILTERS = ["all", "stocks", "indices", "energy", "metals", "fx", "crypto"] as const;
|
|
16
|
-
|
|
21
|
+
/** The venues Gloom collects perpetuals from: what the venue filter and `--venue` take. */
|
|
22
|
+
export const VENUES = ["hyperliquid", "binance", "bybit", "okx"] as const;
|
|
23
|
+
/** Every venue a market identity can name (`kraken:PF_XBTUSD`), collected or not. */
|
|
24
|
+
export const IDENTITY_VENUES = [...VENUES, "deribit", "coinbase", "kraken", "dydx"] as const;
|
|
25
|
+
/** What `--venue` says of a venue Gloom does not collect yet; null for a covered venue and for any other word. */
|
|
26
|
+
export function uncoveredVenueNote(venue: string): string | null {
|
|
27
|
+
const id = venue.trim().toLowerCase();
|
|
28
|
+
return (IDENTITY_VENUES as readonly string[]).includes(id) && !(VENUES as readonly string[]).includes(id)
|
|
29
|
+
? `Gloom doesn't cover ${venueName(id)} perps yet.` : null;
|
|
30
|
+
}
|
|
17
31
|
export const BOARD_SORTS = ["oi", "funding", "oi-change", "premium", "long-short"] as const;
|
|
18
32
|
export const sortLabel = (sort: string) => ({ oi: "OI", funding: "Funding", "oi-change": "OI change", premium: "Premium", "long-short": "Long %" })[sort] ?? sort;
|
|
19
33
|
export const label = (s: string) => s === "fx" ? "FX" : s.charAt(0).toUpperCase() + s.slice(1);
|
|
@@ -5,13 +5,10 @@ import type { PerpBoardRow, PerpMarketPayload } from "../../../api-client/perps"
|
|
|
5
5
|
import { buildSectionedRows, ChartTableHeader, DataTableView, EmptyState, KeyValueRow, SectionHeading, PaneStatusBody, QueryBar, StatGrid, useChartTableSelection,
|
|
6
6
|
usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, useQueryBarSearch, type DataTableCell, type KeyValueRowProps, type PaneFooterSegment, type PaneHint, type SectionedRow,
|
|
7
7
|
type StatItem } from "../../../components";
|
|
8
|
-
import { deriveHeadlessFreshness, paneStatusLineVariants } from "../../../cli/pane-functions/freshness";
|
|
9
8
|
import { openFormModal } from "../../../components/form-modal";
|
|
10
9
|
import { tf } from "../../../i18n";
|
|
11
10
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
12
11
|
import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePaneTitle, usePluginAppActions, usePluginPaneState } from "../../../public/react";
|
|
13
|
-
import { useAgeClock } from "../../../react/auto-refresh";
|
|
14
|
-
import { useRemoteUiNode } from "../../../remote/semantic-tree";
|
|
15
12
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
16
13
|
import type { PaneProps } from "../../../types/plugin";
|
|
17
14
|
import { Box, ScrollBox, useRendererHost, useUiCapabilities } from "../../../ui";
|
|
@@ -25,9 +22,11 @@ import { isCloudSessionRequired, useResearchCloudSession } from "../shared/resea
|
|
|
25
22
|
import { boardSearchText, cachedPerpSelection, cachedPerpsBoard, cachedPerpsCompare, cachedPerpsRankings, loadPerpSelection, loadPerpsBoard, loadPerpsCompare,
|
|
26
23
|
loadPerpsHistoryForRange, loadPerpsRankings } from "./client";
|
|
27
24
|
import { perpFreshness } from "./freshness";
|
|
25
|
+
import { Depth, Liquidations } from "./cascade-tabs";
|
|
26
|
+
import { useFreshnessStatusLine } from "./status-line";
|
|
28
27
|
import { useLivePerpRows, useLivePerpTableRows } from "./live-rows";
|
|
29
28
|
import { aprFormula, ASSET_FILTERS, BOARD_SORTS, boardColumns, collectedVenueCount, compact, compareColumns, dayChange, fundingInterval, fundingSpread, historyCaption, historyCell, historyChange, historyColumns,
|
|
30
|
-
HISTORY_METRICS, historyRows, historySeries, historyValue, inPoints, isMarketTab, label, longShortReason, marketLabel, metricLabel, openingTab, perpCell, perpHistoryProText, PERP_TABS, percent,
|
|
29
|
+
HISTORY_METRICS, historyRows, historySeries, historyValue, inPoints, isAssetTab, isMarketTab, label, longShortReason, marketLabel, metricLabel, openingTab, perpCell, perpHistoryProText, PERP_TABS, percent,
|
|
31
30
|
positioningFields, price, rankingColumns, rankingRows, RANKINGS, ratioText, share, sortLabel, time, underlyingTicker, venueLabel, venueName, venuesOf, type HistoryMetric,
|
|
32
31
|
type PerpColumnId, type PerpTab, type RankingKey } from "./model";
|
|
33
32
|
|
|
@@ -45,17 +44,8 @@ const withRankedRow = (entry: SectionedRow<Ranked>, row: PerpBoardRow): Sectione
|
|
|
45
44
|
* same way.
|
|
46
45
|
*/
|
|
47
46
|
function usePerpStatusLine(rows: readonly PerpBoardRow[], funding: readonly PerpBoardRow[], since: number | null, live = false): PaneFooterSegment[] {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
useRemoteUiNode(declared ? { role: "report-freshness", label: "Venue freshness", getMetadata: () => ({ ...declared }) } : null);
|
|
51
|
-
return useMemo(() => {
|
|
52
|
-
if (!declared) return [];
|
|
53
|
-
const freshness = deriveHeadlessFreshness({ shape: "rows" }, { rows: [], freshness: declared }, now);
|
|
54
|
-
const tone = freshness.status === "stale" ? "warning" as const : "muted" as const;
|
|
55
|
-
// A footer too narrow beside its keys gives up the funding part before any venue's time.
|
|
56
|
-
const [line, ...shorter] = paneStatusLineVariants(freshness).map((text) => [{ text, tone }]);
|
|
57
|
-
return [{ id: "perp-freshness", asOf: true, parts: line!, variants: shorter }];
|
|
58
|
-
}, [declared, rows, now]);
|
|
47
|
+
const declare = useCallback((now: number) => rows.length ? perpFreshness(rows, { funding, now, live }) : null, [rows, funding, live]);
|
|
48
|
+
return useFreshnessStatusLine(declare, since);
|
|
59
49
|
}
|
|
60
50
|
|
|
61
51
|
type EvidenceField = [label: string, value: string, detail?: string, link?: KeyValueRowProps["link"]];
|
|
@@ -378,11 +368,12 @@ export function PerpsPane({ width, height, focused, marketAction }: Pick<PanePro
|
|
|
378
368
|
const openDescription = useOpenDescription();
|
|
379
369
|
const upgrade = useCloudUpgradeAction("perp");
|
|
380
370
|
const [openingMarket] = usePaneSettingValue("market", "");
|
|
381
|
-
const
|
|
371
|
+
const opening = openingTab(openingMarket, access.hasProAccess);
|
|
372
|
+
const [openingTabValue] = usePaneSettingValue("tab", opening);
|
|
382
373
|
// A market asked for again (a stock's perpetual row, PERP) comes back to it on its opening tab, whatever this pane
|
|
383
374
|
// shows by then.
|
|
384
375
|
const [savedTab, setTab] = useCommandValue<string>("perps", "perps:tab", (params) => params.market ? openingTabValue : null, openingTabValue);
|
|
385
|
-
const tab = PERP_TABS.includes(savedTab as PerpTab) ? savedTab as PerpTab :
|
|
376
|
+
const tab = PERP_TABS.includes(savedTab as PerpTab) ? savedTab as PerpTab : opening;
|
|
386
377
|
const marketTab = isMarketTab(tab);
|
|
387
378
|
const [search, setSearch] = useCommandValue("perps", "perps:search", (params) => params.market || null, openingMarket);
|
|
388
379
|
const [compareAsset, setCompareAsset] = usePluginPaneState<string>("perps:compare", compareOpening(openingMarket));
|
|
@@ -409,13 +400,17 @@ export function PerpsPane({ width, height, focused, marketAction }: Pick<PanePro
|
|
|
409
400
|
// The market on screen goes with the user: History and Evidence open the row picked on a list, Compare its base asset.
|
|
410
401
|
const current = marketTab ? active : listRow;
|
|
411
402
|
const carried = marketTab ? active : listRow ?? picked;
|
|
403
|
+
// Liquidations and Depth list no board rows: a market tab after them opens their asset, and they keep Compare's.
|
|
404
|
+
const assetOnly = tab === "liquidations" || tab === "depth";
|
|
412
405
|
const selectTab = useCallback((next: string) => {
|
|
413
|
-
if (
|
|
414
|
-
if (carried && next
|
|
406
|
+
if (assetOnly && isMarketTab(next)) setSearch(compareAsset);
|
|
407
|
+
else if (carried && isMarketTab(next) && !marketTab) setSearch(carried.marketId);
|
|
408
|
+
if (carried && isAssetTab(next) && !assetOnly) setCompareAsset(carried.baseAsset);
|
|
415
409
|
setListRow(null);
|
|
416
410
|
setTab(next);
|
|
417
|
-
}, [carried, marketTab, setSearch, setCompareAsset, setTab]);
|
|
418
|
-
const openMarket = useCallback((row: PerpBoardRow, next: PerpTab =
|
|
411
|
+
}, [assetOnly, carried, compareAsset, marketTab, setSearch, setCompareAsset, setTab]);
|
|
412
|
+
const openMarket = useCallback((row: PerpBoardRow, next: PerpTab = openingTab(row.marketId, access.hasProAccess)) => { setSearch(row.marketId); setTab(next); }, [setSearch, setTab, access.hasProAccess]);
|
|
413
|
+
const openMarketId = useCallback((marketId: string) => { setSearch(marketId); setTab(openingTab(marketId, access.hasProAccess)); }, [setSearch, setTab, access.hasProAccess]);
|
|
419
414
|
const { strip, rows: tabRows } = usePaneTabs({ tabs: PERP_TABS.map((value) => ({ value, label: label(value) })), activeValue: tab, onSelect: selectTab, focused, dense: true });
|
|
420
415
|
const refreshMarket = useCallback(() => { void resource.reload(); }, [resource.reload]);
|
|
421
416
|
useAutoRefresh(resource.updatedAt, resource.load, { intervalMs: 60_000 });
|
|
@@ -450,7 +445,9 @@ export function PerpsPane({ width, height, focused, marketAction }: Pick<PanePro
|
|
|
450
445
|
return <Box width={width} height={height} flexDirection="column">
|
|
451
446
|
{strip}
|
|
452
447
|
{tab === "board" ? <Board {...list} /> : tab === "rankings" ? <Rankings {...list} />
|
|
453
|
-
: tab === "compare" ? <Compare {...list} asset={compareAsset} onAssetChange={setCompareAsset} />
|
|
448
|
+
: tab === "compare" ? <Compare {...list} asset={compareAsset} onAssetChange={setCompareAsset} />
|
|
449
|
+
: tab === "liquidations" ? <Liquidations accessKey={accessKey} width={width} height={listHeight} focused={focused} asset={compareAsset} onAssetChange={setCompareAsset} onOpenMarket={openMarketId} />
|
|
450
|
+
: tab === "depth" ? <Depth accessKey={accessKey} width={width} height={listHeight} focused={focused} asset={compareAsset} onAssetChange={setCompareAsset} onOpenMarket={openMarketId} /> : <>
|
|
454
451
|
<QueryBar width={width} search={{ focused, value: search, onChange: setSearch, placeholder: "Market or canonical identity", debounceMs: 300, ...querySearch.searchProps }}
|
|
455
452
|
filters={tab === "history" ? [
|
|
456
453
|
{ id: "metric", label: "Series", value: metric, options: HISTORY_METRICS.map((value) => ({ value, label: metricLabel(value) })), onChange: setMetric },
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { deriveHeadlessFreshness, paneStatusLineVariants } from "../../../cli/pane-functions/freshness";
|
|
3
|
+
import type { PaneFooterSegment } from "../../../components";
|
|
4
|
+
import { useAgeClock } from "../../../react/auto-refresh";
|
|
5
|
+
import { useRemoteUiNode } from "../../../remote/semantic-tree";
|
|
6
|
+
import type { HeadlessPaneFreshness } from "../../../types/headless";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The footer line a tab ends with, from the same freshness its text report declares (`declare`, null while there is
|
|
10
|
+
* nothing to date): the source, its time and how old it is, the line `fn PERP` and `shot PERP` end with. It keeps ageing
|
|
11
|
+
* while the pane is seen (`since` is when the data loaded), and is published for a capture to date itself the same way.
|
|
12
|
+
*/
|
|
13
|
+
export function useFreshnessStatusLine(declare: ((now: number) => HeadlessPaneFreshness | null) | null, since: number | null): PaneFooterSegment[] {
|
|
14
|
+
const now = useAgeClock(since);
|
|
15
|
+
const declared = useMemo(() => declare?.(now) ?? null, [declare, now]);
|
|
16
|
+
useRemoteUiNode(declared ? { role: "report-freshness", label: "Venue freshness", getMetadata: () => ({ ...declared }) } : null);
|
|
17
|
+
return useMemo(() => {
|
|
18
|
+
if (!declared) return [];
|
|
19
|
+
const freshness = deriveHeadlessFreshness({ shape: "rows" }, { rows: [], freshness: declared }, now);
|
|
20
|
+
const tone = freshness.status === "stale" ? "warning" as const : "muted" as const;
|
|
21
|
+
// A footer too narrow beside its keys gives up the note before any venue's time.
|
|
22
|
+
const [line, ...shorter] = paneStatusLineVariants(freshness).map((text) => [{ text, tone }]);
|
|
23
|
+
return [{ id: "perp-freshness", asOf: true, parts: line!, variants: shorter }];
|
|
24
|
+
}, [declared, now]);
|
|
25
|
+
}
|
|
@@ -161,7 +161,11 @@ export function composeBuiltinPlugin(options: CompositePluginOptions): GloomPlug
|
|
|
161
161
|
for (const module of modules) {
|
|
162
162
|
startedModules.push(module);
|
|
163
163
|
try {
|
|
164
|
-
|
|
164
|
+
// Only an async setup is waited for. Awaiting a synchronous one hands
|
|
165
|
+
// the tick to the panes first: Gloom Cloud restores the saved session
|
|
166
|
+
// in its second module, and the panes' first requests went out without it.
|
|
167
|
+
const pending = module.setup?.(ctx);
|
|
168
|
+
if (pending) await pending;
|
|
165
169
|
} catch (error) {
|
|
166
170
|
// Logged as the plugin, so the failure counts toward its health.
|
|
167
171
|
ctx.log.error("Module setup failed", { error: error instanceof Error ? error.message : String(error) });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
1
2
|
import { peekPersistedBrokerAccounts } from "../../../brokers/account-cache";
|
|
2
3
|
import type { AppResourceStorePort } from "../../../core/app-service-ports";
|
|
3
4
|
import type { AppConfig } from "../../../types/config";
|
|
@@ -20,8 +21,9 @@ export function peekCachedBrokerAccounts(
|
|
|
20
21
|
try {
|
|
21
22
|
const accounts = peekPersistedBrokerAccounts(resources, instance);
|
|
22
23
|
if (accounts?.length) brokerAccounts[instance.id] = accounts;
|
|
23
|
-
} catch {
|
|
24
|
+
} catch (error) {
|
|
24
25
|
// An unreadable snapshot leaves the portfolio without account figures.
|
|
26
|
+
logFailure("portfolio-list.cached-account", "Read cached broker accounts failed")(error);
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
return brokerAccounts;
|
|
@@ -8,6 +8,7 @@ import { splitSymbolArgs, splitSymbolWords, symbolInputKey } from "../../../../c
|
|
|
8
8
|
import { notASymbolError } from "../../../../cli/symbol-cells";
|
|
9
9
|
import { readSymbolList, SymbolListError } from "../../../../cli/symbol-list";
|
|
10
10
|
import { cliStyles } from "../../../../utils/cli-output";
|
|
11
|
+
import { shellWord } from "../../../../utils/shell-word";
|
|
11
12
|
import { dryRunNote } from "../../../../cli/helpers";
|
|
12
13
|
import {
|
|
13
14
|
addSymbol,
|
|
@@ -46,14 +47,7 @@ export const SYMBOL_LIST_HELP = {
|
|
|
46
47
|
],
|
|
47
48
|
};
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
* One word of a command line as a shell reads it back: as written when it
|
|
51
|
-
* holds nothing a shell treats specially (`NVDA`, `SAN:EPA`, `BRK.B`), else
|
|
52
|
-
* in single quotes (`'Altria Group'`, `'$nvda'`, `'AT&T'`).
|
|
53
|
-
*/
|
|
54
|
-
export function shellWord(word: string): string {
|
|
55
|
-
return /^[\p{L}\p{N}._:@%+,/][\p{L}\p{N}._:@%+,/=-]*$/u.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
56
|
-
}
|
|
50
|
+
export { shellWord };
|
|
57
51
|
|
|
58
52
|
/**
|
|
59
53
|
* A row's line, and under it the listing a company's name was read as, the
|
|
@@ -50,7 +50,7 @@ import { importSource, notionalPositions, rankThirteenFLines, type ThirteenFImpo
|
|
|
50
50
|
import { importedFromText } from "../imported-from";
|
|
51
51
|
import { savedListingCoinNote } from "../../../../cli/commands/crypto-hints";
|
|
52
52
|
import { findNamedCollection } from "../../../../portfolio/collection-name";
|
|
53
|
-
import {
|
|
53
|
+
import { ambiguousQuantityProblem } from "../../../../portfolio/position-quantity";
|
|
54
54
|
import {
|
|
55
55
|
CLI_COST_FX_WORDING, loadPurchaseDateCostFx, readCostFxRate, readPurchaseDate, resolvePositionCostFx, type CostFxHistoryClient,
|
|
56
56
|
} from "../../../../portfolio/cost-fx";
|
|
@@ -363,8 +363,8 @@ async function setPositionCommand(
|
|
|
363
363
|
const avgCost = parseAmountArg(avgCostValue, "Average cost");
|
|
364
364
|
// The notes say which listing a bare symbol went to, and that a bare coin ticker is not the coin, as `portfolio add` does.
|
|
365
365
|
const { ticker, notes } = await resolveCliTicker(symbol, store, dataProvider, exchange);
|
|
366
|
-
// `1.250`
|
|
367
|
-
const lots =
|
|
366
|
+
// `1.250` and `1.000` ask which was meant, as `1,250` did above: a dot before three digits is as often thousands.
|
|
367
|
+
const lots = ambiguousQuantityProblem(sharesValue, ticker, { listing: savedListingName(ticker).key });
|
|
368
368
|
if (lots) throw new PortfolioCliError(lots);
|
|
369
369
|
// A future is held in contracts of the size its terms give, as `quote` states it, and a cost typed as
|
|
370
370
|
// corn is quoted counts cents.
|
|
@@ -942,7 +942,7 @@ const AMOUNTS_HELP = {
|
|
|
942
942
|
lines: [
|
|
943
943
|
"Numbers take either decimal mark: 1.234,5, 1234,5 and 1,234.5 are 1234.5, and 18,50 is 18.5.",
|
|
944
944
|
"Cash and --notional are amounts of a currency (cash in the portfolio's unless named, --notional in USD), so 500,000 and 1.234 are thousands, a third decimal being below a cent. In a currency with three decimals (KWD, BHD, OMR) they could be either, and the command asks which.",
|
|
945
|
-
"
|
|
945
|
+
"Average cost reads a lone dot as a decimal point (123.456). Shares and contracts ask which a lone dot or comma before three digits meant (1.000, 1,234), thousands or a decimal, and name both readings to type.",
|
|
946
946
|
],
|
|
947
947
|
};
|
|
948
948
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { createBaseConverter } from "../../../../cli/base-converter";
|
|
2
3
|
import { savedListingName } from "../../../../cli/listing-arg";
|
|
3
4
|
import { currentListingName } from "../../../../tickers/listing-name";
|
|
@@ -257,8 +258,9 @@ export async function loadCollectionQuotes(
|
|
|
257
258
|
const quote = await dataProvider.getQuote(ticker.metadata.ticker, ticker.metadata.exchange,
|
|
258
259
|
datesChangeBasis ? { ...context, datesChangeBasis } : context);
|
|
259
260
|
quotes.set(ticker.metadata.ticker, quote);
|
|
260
|
-
} catch {
|
|
261
|
-
//
|
|
261
|
+
} catch (error) {
|
|
262
|
+
// A partial quote failure leaves the rest of the table rendering.
|
|
263
|
+
logFailure("portfolio-list.cli.render", "Load collection quote failed")(error);
|
|
262
264
|
}
|
|
263
265
|
}),
|
|
264
266
|
);
|
|
@@ -1269,7 +1271,7 @@ async function loadMarketCapFinancials(
|
|
|
1269
1271
|
const targets = needed.map((ticker) => ({ symbol: ticker.metadata.ticker, exchange: ticker.metadata.exchange }));
|
|
1270
1272
|
if (dataProvider.getTickerFinancialsBatch) {
|
|
1271
1273
|
await Promise.all(chunked(targets, FINANCIALS_BATCH_SIZE).map(async (batch) => {
|
|
1272
|
-
const results = await dataProvider.getTickerFinancialsBatch!(batch).catch((
|
|
1274
|
+
const results = await dataProvider.getTickerFinancialsBatch!(batch).catch(logFailure("portfolio-list.cli.render", "Load market cap financials batch failed", []));
|
|
1273
1275
|
batch.forEach((target, index) => {
|
|
1274
1276
|
const value = (results.find((result) => result.target === target) ?? results[index])?.financials;
|
|
1275
1277
|
if (value) financials.set(target.symbol, value);
|
|
@@ -1278,7 +1280,7 @@ async function loadMarketCapFinancials(
|
|
|
1278
1280
|
return financials;
|
|
1279
1281
|
}
|
|
1280
1282
|
await mapWithConcurrency(targets, FINANCIALS_CONCURRENCY, async (target) => {
|
|
1281
|
-
const value = await dataProvider.getTickerFinancials(target.symbol, target.exchange).catch((
|
|
1283
|
+
const value = await dataProvider.getTickerFinancials(target.symbol, target.exchange).catch(logFailure("portfolio-list.cli.render", "Load market cap financials failed", null));
|
|
1282
1284
|
if (value) financials.set(target.symbol, value);
|
|
1283
1285
|
});
|
|
1284
1286
|
return financials;
|
|
@@ -1298,7 +1300,7 @@ async function loadPerpetualFunding(tickers: readonly TickerRecord[]): Promise<M
|
|
|
1298
1300
|
});
|
|
1299
1301
|
const funding = new Map<string, number | null>();
|
|
1300
1302
|
await mapWithConcurrency(markets, PERPETUAL_CONCURRENCY, async ({ symbol, listing }) => {
|
|
1301
|
-
funding.set(symbol, await loadPerpetualMarket(listing).then((market) => market?.row.fundingRate8h ?? null, (
|
|
1303
|
+
funding.set(symbol, await loadPerpetualMarket(listing).then((market) => market?.row.fundingRate8h ?? null, logFailure("portfolio-list.cli.render", "Load perpetual funding failed", null)));
|
|
1302
1304
|
});
|
|
1303
1305
|
return funding;
|
|
1304
1306
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEffect, useMemo } from "react";
|
|
2
|
+
import { getSharedMarketDataCoordinator } from "../../../../market-data/coordinator";
|
|
3
|
+
import { formatMarketPrice, quoteFormatOptions, withCurrencyMinorDigits } from "../../../../market-data/market/format";
|
|
4
|
+
import { buildQuoteKey, instrumentFromQuoteTarget, resolveEntryData } from "../../../../market-data/selectors";
|
|
5
|
+
import type { PortfolioFuturesTerms } from "../../../../portfolio/futures-positions";
|
|
6
|
+
import { useLiveQuoteEntries } from "../../../../state/hooks/quote-streaming";
|
|
7
|
+
import type { QuoteSubscriptionTarget } from "../../../../types/data-provider";
|
|
8
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
9
|
+
|
|
10
|
+
/** Front references share one snapshot and stream per contract, independent of each row's own price. */
|
|
11
|
+
export function useFuturesFrontNotes(
|
|
12
|
+
terms: ReadonlyMap<string, PortfolioFuturesTerms>,
|
|
13
|
+
{ enabled, liveStreaming }: { enabled: boolean; liveStreaming: boolean },
|
|
14
|
+
): ReadonlyMap<string, string> {
|
|
15
|
+
const symbolsKey = useMemo(() => enabled
|
|
16
|
+
? [...new Set([...terms.values()].flatMap((value) => value.front?.contractSymbol ?? []))].sort().join("\u001f") : "", [enabled, terms]);
|
|
17
|
+
const targets = useMemo<QuoteSubscriptionTarget[]>(() => symbolsKey ? symbolsKey.split("\u001f").map((symbol) => ({
|
|
18
|
+
symbol, exchange: "", route: "provider", surface: "watchlist", visible: true,
|
|
19
|
+
})) : [], [symbolsKey]);
|
|
20
|
+
const instruments = useMemo(() => targets.map(instrumentFromQuoteTarget), [targets]);
|
|
21
|
+
const coordinator = getSharedMarketDataCoordinator();
|
|
22
|
+
const { entries } = useLiveQuoteEntries(targets, { liveStreaming });
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
// Polling primes its own batch immediately; a stream needs one snapshot while connecting.
|
|
25
|
+
if (!liveStreaming || !coordinator || !instruments.length) return;
|
|
26
|
+
void coordinator.loadQuotesBatch(instruments).catch(logFailure("portfolio-futures", "Read front contract quotes failed"));
|
|
27
|
+
}, [coordinator, instruments, liveStreaming]);
|
|
28
|
+
return useMemo(() => {
|
|
29
|
+
const notes = new Map<string, string>();
|
|
30
|
+
if (!enabled) return notes;
|
|
31
|
+
for (const [symbol, value] of terms) {
|
|
32
|
+
if (!value.front) continue;
|
|
33
|
+
const quote = resolveEntryData(entries.get(buildQuoteKey({ symbol: value.front.contractSymbol, exchange: "" })));
|
|
34
|
+
if (!quote || !Number.isFinite(quote.price)) continue;
|
|
35
|
+
const options = withCurrencyMinorDigits(quoteFormatOptions(quote, "FUTURE"), quote.currency);
|
|
36
|
+
notes.set(symbol, `front ${value.front.contract} ${formatMarketPrice(quote.price, options)}`);
|
|
37
|
+
}
|
|
38
|
+
return notes;
|
|
39
|
+
}, [enabled, entries, terms]);
|
|
40
|
+
}
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
removeTickerFromCollection,
|
|
80
80
|
tickerRemovalSummary,
|
|
81
81
|
} from "./remove-ticker";
|
|
82
|
-
import { QuickAddTickerInput, type QuickAddCollectionKind } from "../quick-add";
|
|
82
|
+
import { QUICK_ADD_KEY, QuickAddTickerInput, type QuickAddCollectionKind } from "../quick-add";
|
|
83
83
|
import { delistingLabel } from "../../../../market-data/quotes/delisting";
|
|
84
84
|
import {
|
|
85
85
|
buildTrackedCurrencies,
|
|
@@ -104,6 +104,7 @@ import { hasUnknownOptionMultiplier } from "../../../../portfolio/position-metri
|
|
|
104
104
|
import { withFuturesContractSizes } from "../../../../portfolio/futures-positions";
|
|
105
105
|
import { withPurchaseDateCostFx } from "../../../../portfolio/cost-fx";
|
|
106
106
|
import { usePortfolioFuturesTerms } from "./futures-terms";
|
|
107
|
+
import { useFuturesFrontNotes } from "./futures-front";
|
|
107
108
|
import { usePurchaseDateCostFx } from "./purchase-date-fx";
|
|
108
109
|
import {
|
|
109
110
|
allocationWeightBase,
|
|
@@ -142,8 +143,6 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
142
143
|
|
|
143
144
|
const [streamWindow, setStreamWindow] = useState({ start: 0, end: 24 });
|
|
144
145
|
const [quickAddFocused, setQuickAddFocused] = useState(false);
|
|
145
|
-
// Bumped by the empty table's "+ ticker" button to open the add row.
|
|
146
|
-
const [quickAddRequest, setQuickAddRequest] = useState(0);
|
|
147
146
|
|
|
148
147
|
const {
|
|
149
148
|
cursorSymbol,
|
|
@@ -185,7 +184,8 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
185
184
|
[config.baseCurrency, currentPortfolio],
|
|
186
185
|
);
|
|
187
186
|
// A future counts contracts of the size its terms give, and at the close is valued at its settlement.
|
|
188
|
-
const futuresTerms = usePortfolioFuturesTerms(collectionTickers, { enabled:
|
|
187
|
+
const futuresTerms = usePortfolioFuturesTerms(collectionTickers, { enabled: true, settlement: isPortfolioTab && valuationBasis === "regular-close" });
|
|
188
|
+
const futuresFrontNotes = useFuturesFrontNotes(futuresTerms.terms, { enabled: appActive && viewMode === "table", liveStreaming });
|
|
189
189
|
// A dated lot in another currency costs what its purchase date's FX close says in that currency, as portfolio show values it.
|
|
190
190
|
const purchaseDateFx = usePurchaseDateCostFx(collectionTickers, { enabled: isPortfolioTab, portfolioId: activeCollectionId, into: totalsCurrency });
|
|
191
191
|
const tickers = useMemo(
|
|
@@ -735,7 +735,6 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
735
735
|
return null;
|
|
736
736
|
}, [activeCollectionId, config, currentPortfolio]);
|
|
737
737
|
const showQuickAdd = !!(activeCollectionId && activeCollectionEntry && quickAddCollectionKind);
|
|
738
|
-
const requestQuickAdd = useCallback(() => setQuickAddRequest((count) => count + 1), []);
|
|
739
738
|
const quickAddHeight = showQuickAdd ? 1 : 0;
|
|
740
739
|
const selectedFinancials = cursorSymbol ? financialsMap.get(cursorSymbol) : undefined;
|
|
741
740
|
const selectedCap = liveMarketCapitalization(selectedFinancials?.quote, selectedFinancials?.fundamentals);
|
|
@@ -763,7 +762,6 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
763
762
|
width={width}
|
|
764
763
|
onAdded={handleTickerAdded}
|
|
765
764
|
onFocusChange={setQuickAddFocused}
|
|
766
|
-
focusRequest={quickAddRequest}
|
|
767
765
|
/>
|
|
768
766
|
) : null;
|
|
769
767
|
|
|
@@ -796,6 +794,7 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
796
794
|
onCursorChange={handleCursorChange}
|
|
797
795
|
financialsMap={financialsMap}
|
|
798
796
|
columnContext={columnContext}
|
|
797
|
+
futuresFrontNotes={futuresFrontNotes}
|
|
799
798
|
flashSymbols={flashSymbols}
|
|
800
799
|
sinceLastSeen={sinceLastSeen}
|
|
801
800
|
onRootKeyDown={handleTableKeyDown}
|
|
@@ -804,7 +803,7 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
804
803
|
visibleRangeBuffer={3}
|
|
805
804
|
resetScrollKey={activeCollectionId}
|
|
806
805
|
onRowActivate={handleRowActivate}
|
|
807
|
-
|
|
806
|
+
addTickerShortcut={showQuickAdd ? QUICK_ADD_KEY : undefined}
|
|
808
807
|
onOpenPerpetual={showsPerpetuals ? openPerpMarket : undefined}
|
|
809
808
|
rootHeight={contentHeight}
|
|
810
809
|
/>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { useEffect, useMemo, useRef } from "react";
|
|
2
3
|
import { getSharedMarketDataCoordinator } from "../../../../market-data/coordinator";
|
|
3
4
|
import { instrumentFromTicker, quoteSubscriptionTargetFromTicker } from "../../../../market-data/request-types";
|
|
@@ -251,8 +252,9 @@ export function usePortfolioPaneStreaming({
|
|
|
251
252
|
? sharedCoordinator.loadSnapshotsBatch(normalSnapshotEntries.map((entry) => entry.instrument))
|
|
252
253
|
: Promise.resolve(),
|
|
253
254
|
]);
|
|
254
|
-
} catch {
|
|
255
|
+
} catch (error) {
|
|
255
256
|
// Best-effort warmup for visible rows only.
|
|
257
|
+
logFailure("portfolio-list.pane.streaming", "Warm visible portfolio rows failed")(error);
|
|
256
258
|
} finally {
|
|
257
259
|
for (const entry of [...quoteEntries, ...forcedSnapshotEntries, ...normalSnapshotEntries]) {
|
|
258
260
|
warmupInFlightRef.current.delete(entry.key);
|
|
@@ -304,8 +306,9 @@ export function usePortfolioPaneStreaming({
|
|
|
304
306
|
|
|
305
307
|
try {
|
|
306
308
|
await sharedCoordinator.loadQuotesBatch(quoteEntries.map((entry) => entry.instrument), { forceRefresh: true });
|
|
307
|
-
} catch {
|
|
309
|
+
} catch (error) {
|
|
308
310
|
// Best-effort watchdog for visible rows only.
|
|
311
|
+
logFailure("portfolio-list.pane.streaming", "Refresh visible portfolio quotes failed")(error);
|
|
309
312
|
} finally {
|
|
310
313
|
for (const entry of quoteEntries) {
|
|
311
314
|
warmupInFlightRef.current.delete(entry.key);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { useEffect, useMemo, useRef, useState, type Dispatch, type SetStateAction } from "react";
|
|
2
3
|
import type { ColumnConfig } from "../../../../types/config";
|
|
3
4
|
import type { AnalystResearchData, CorporateActionsData, TickerFinancials } from "../../../../types/financials";
|
|
@@ -171,7 +172,7 @@ function useAnalystSupplemental(
|
|
|
171
172
|
}
|
|
172
173
|
})).then((entries) => {
|
|
173
174
|
if (mountedRef.current) setState((current) => setAnalystValues(current, entries));
|
|
174
|
-
}).catch((
|
|
175
|
+
}).catch(logFailure("portfolio-list.pane.supplemental", "Apply analyst research failed"));
|
|
175
176
|
}, [enabled, mountedRef, provider, setState, state.analystResearch, targetsKey]);
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -211,7 +212,7 @@ function useCorporateSupplemental(
|
|
|
211
212
|
}
|
|
212
213
|
})).then((entries) => {
|
|
213
214
|
if (mountedRef.current) setState((current) => setCorporateValues(current, entries));
|
|
214
|
-
}).catch((
|
|
215
|
+
}).catch(logFailure("portfolio-list.pane.supplemental", "Apply corporate actions failed"));
|
|
215
216
|
}, [enabled, mountedRef, provider, setState, state.corporateActions, targetsKey]);
|
|
216
217
|
}
|
|
217
218
|
|
|
@@ -112,7 +112,6 @@ export function QuickAddTickerInput({
|
|
|
112
112
|
width,
|
|
113
113
|
onAdded,
|
|
114
114
|
onFocusChange,
|
|
115
|
-
focusRequest = 0,
|
|
116
115
|
}: {
|
|
117
116
|
collectionId: string;
|
|
118
117
|
collectionKind: QuickAddCollectionKind;
|
|
@@ -121,12 +120,6 @@ export function QuickAddTickerInput({
|
|
|
121
120
|
width: number;
|
|
122
121
|
onAdded: (symbol: string) => void;
|
|
123
122
|
onFocusChange?: (focused: boolean) => void;
|
|
124
|
-
/**
|
|
125
|
-
* Bumped by a control outside the row, such as the empty table's
|
|
126
|
-
* "+ ticker" button, to open it. A click there focuses the pane in the same
|
|
127
|
-
* press, so the row opens once the pane is focused.
|
|
128
|
-
*/
|
|
129
|
-
focusRequest?: number;
|
|
130
123
|
}) {
|
|
131
124
|
const dispatch = useAppDispatch();
|
|
132
125
|
const { notify } = usePluginAppActions();
|
|
@@ -150,13 +143,6 @@ export function QuickAddTickerInput({
|
|
|
150
143
|
setInputFocused(false);
|
|
151
144
|
}, []);
|
|
152
145
|
|
|
153
|
-
const handledFocusRequestRef = useRef(focusRequest);
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
if (focusRequest === handledFocusRequestRef.current || !focused) return;
|
|
156
|
-
handledFocusRequestRef.current = focusRequest;
|
|
157
|
-
focusInput();
|
|
158
|
-
}, [focusInput, focusRequest, focused]);
|
|
159
|
-
|
|
160
146
|
// A symbol with no listing leaves the field, so the next one typed replaces
|
|
161
147
|
// it instead of running on (OMV.VIREP.MC); the line under it still says what
|
|
162
148
|
// failed until something is typed.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { getSharedRegistry } from "../../../registry";
|
|
2
3
|
import {
|
|
3
4
|
AmbiguousContractError,
|
|
@@ -21,7 +22,8 @@ import {
|
|
|
21
22
|
} from "../../../../tickers/ticker-changes";
|
|
22
23
|
|
|
23
24
|
const QUICK_ADD_MAX_QUERY_LENGTH = 32;
|
|
24
|
-
|
|
25
|
+
// A slash writes a pair as crypto exchanges and FX desks do: BTC/GBP, EUR/USD.
|
|
26
|
+
const QUICK_ADD_SYMBOL_RE = /^[A-Z0-9][A-Z0-9.\-/\s]*$/;
|
|
25
27
|
|
|
26
28
|
export type QuickAddCollectionKind = "portfolio" | "watchlist";
|
|
27
29
|
|
|
@@ -178,7 +180,8 @@ export async function resolveQuickAddValidation({
|
|
|
178
180
|
exchangeFromResolved(resolved),
|
|
179
181
|
quoteContextFromResolved(resolved),
|
|
180
182
|
);
|
|
181
|
-
} catch {
|
|
183
|
+
} catch (error) {
|
|
184
|
+
logFailure("portfolio-list.quick-add.resolution", "Read quick-add quote failed")(error);
|
|
182
185
|
quote = null;
|
|
183
186
|
}
|
|
184
187
|
}
|