@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
|
+
import { createTtlCache } from "../../../data/ttl-cache";
|
|
2
3
|
import { getSharedMarketDataCoordinator, MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
|
|
3
4
|
import type { InstrumentRef, OptionsRequest } from "../../../market-data/request-types";
|
|
4
5
|
import type { QueryEntry } from "../../../market-data/result-types";
|
|
@@ -73,19 +74,8 @@ export function withReusedYieldCurve(
|
|
|
73
74
|
dependencies: SurfaceLoaderDependencies,
|
|
74
75
|
reuseMs = TREASURY_CURVE_REUSE_MS,
|
|
75
76
|
): SurfaceLoaderDependencies {
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
...dependencies,
|
|
79
|
-
loadYieldCurve: () => {
|
|
80
|
-
const now = Date.now();
|
|
81
|
-
if (held && now - held.at < reuseMs) return held.curve;
|
|
82
|
-
const curve = dependencies.loadYieldCurve();
|
|
83
|
-
const entry = { at: now, curve };
|
|
84
|
-
held = entry;
|
|
85
|
-
curve.catch(() => { if (held === entry) held = null; });
|
|
86
|
-
return curve;
|
|
87
|
-
},
|
|
88
|
-
};
|
|
77
|
+
const curves = createTtlCache<YieldPoint[]>({ ttlMs: reuseMs });
|
|
78
|
+
return { ...dependencies, loadYieldCurve: () => curves.load("curve", () => dependencies.loadYieldCurve()) };
|
|
89
79
|
}
|
|
90
80
|
|
|
91
81
|
export interface SurfaceLoadRequest {
|
|
@@ -12,7 +12,7 @@ import { createSurfaceDependencies, loadVolatilitySurface } from "./client";
|
|
|
12
12
|
import { buildSurfaceGrid, type SurfaceGridOptions, type SurfaceSettings, type SurfaceSnapshot } from "./model";
|
|
13
13
|
import { atCapturePrecision, formatCaptureTime, loadDatedSurface, storedSurfaceDate, type DatedSurfaceSnapshot } from "./stored";
|
|
14
14
|
import { listingFateEmpty, loadListingFate } from "../../../tickers/listing-fate";
|
|
15
|
-
import { coinOptionsAlias, coinWithoutOptionsRefusal } from "../../../market-data/options-alternatives";
|
|
15
|
+
import { coinOptionsAlias, coinWithoutOptionsRefusal, fxVolatilityRefusal } from "../../../market-data/options-alternatives";
|
|
16
16
|
import { formatUtcTime } from "../../../utils/utc-time";
|
|
17
17
|
import { dvolCurrency, dvolGap, dvolSeriesId, loadDvol, type DvolPayload } from "../volatility/dvol";
|
|
18
18
|
import {
|
|
@@ -118,7 +118,7 @@ function skewSection(snapshot: SurfaceSnapshot): HeadlessBundleSection {
|
|
|
118
118
|
return { title: "Skew", columns: [
|
|
119
119
|
{ key: "expiry", header: "Expiry" }, { key: "days", header: "Days", format: days },
|
|
120
120
|
{ key: "put25", header: "25D put IV", format: iv }, { key: "call25", header: "25D call IV", format: iv },
|
|
121
|
-
// Each difference names its sign: the risk reversal is call minus put
|
|
121
|
+
// Each difference names its sign: the risk reversal is call minus put here and in VCA and HIVG's rr25d.
|
|
122
122
|
{ key: "riskReversal", header: skewHeader("RR", "call-put"), format: gap }, { key: "butterfly", header: skewHeader("BF", "wings-ATM"), format: gap },
|
|
123
123
|
{ key: "moneynessSkew", header: skewHeader("90/110", "90%-110%"), format: (value, row) => typeof value === "number" ? points(value)
|
|
124
124
|
: (row as unknown as SurfaceSkewRow).moneynessGap ?? "--" },
|
|
@@ -169,7 +169,7 @@ export const volSurfaceHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
169
169
|
shape: "bundle", argument: { kind: "ticker", description: "Underlying ticker" },
|
|
170
170
|
usListingsOnly: "Options data",
|
|
171
171
|
symbolAlias: coinOptionsAlias("OVDV"),
|
|
172
|
-
refuseSymbol: coinWithoutOptionsRefusal("OVDV"),
|
|
172
|
+
refuseSymbol: (key) => coinWithoutOptionsRefusal("OVDV")(key) ?? fxVolatilityRefusal("OVDV")(key),
|
|
173
173
|
discovery: { suggestFor: IMPLIED_VOL_WORDS },
|
|
174
174
|
describe: (args) => `OVDV ${args.symbols[0] ?? ""}`,
|
|
175
175
|
options: [
|
|
@@ -39,7 +39,7 @@ export const volSurfaceModule: PluginModule = {
|
|
|
39
39
|
paneTemplates: [createTickerSurfacePaneTemplate({
|
|
40
40
|
id: "vol-surface-pane", paneId: "vol-surface", label: "Volatility Surface",
|
|
41
41
|
description: "Implied volatility surface, smiles, term structure, skew (25-delta risk reversal and butterfly) and forwards; DVOL beside BTC-USD and ETH-USD.",
|
|
42
|
-
keywords: ["ovdv", "volatility", "surface", "smile", "skew", "options", "risk reversal", "butterfly", "25 delta", "dvol", "deribit", "implied volatility index"],
|
|
42
|
+
keywords: ["ovdv", "volatility", "vol", "surface", "smile", "skew", "options", "risk reversal", "butterfly", "25 delta", "dvol", "deribit", "implied volatility index"],
|
|
43
43
|
shortcut: "OVDV", publicShare: true,
|
|
44
44
|
settings: (_symbol, _context, options) => options?.values?.expiration
|
|
45
45
|
? { expiration: Number(options.values.expiration) } : {},
|
|
@@ -43,7 +43,7 @@ import { expiryLabel, formatIv, formatPrice, formatVolPoints, HIDDEN_SLOPE_TEXT,
|
|
|
43
43
|
surfaceSkewRow, surfaceSpotBasisLabel } from "./tables";
|
|
44
44
|
import { findListedExpiry, unlistedExpiryText } from "../../../utils/option-expiry";
|
|
45
45
|
import { dvolCurrency, dvolGap, loadDvol, type DvolPayload } from "../volatility/dvol";
|
|
46
|
-
import { COIN_OPTIONS_HINT, isCoinWithoutOptions, optionsUnavailableTitle } from "../../../market-data/options-alternatives";
|
|
46
|
+
import { COIN_OPTIONS_HINT, fxVolatilityNote, isCoinWithoutOptions, optionsUnavailableTitle } from "../../../market-data/options-alternatives";
|
|
47
47
|
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
48
48
|
import { surfaceThirtyDayAtm } from "./tables";
|
|
49
49
|
|
|
@@ -191,9 +191,10 @@ export function VolSurfacePane({ focused, width, height }: PaneProps) {
|
|
|
191
191
|
const fateExchange = target?.effectiveExchange ?? "";
|
|
192
192
|
const fateLoader = useCallback(async () => (await getSharedMarketDataCoordinator()
|
|
193
193
|
?.loadListingFate({ symbol: underlying, exchange: fateExchange }))?.note ?? null, [underlying, fateExchange]);
|
|
194
|
-
// A coin nothing lists options on (SOL-USD) says which coins have them.
|
|
194
|
+
// A coin nothing lists options on (SOL-USD) says which coins have them, and an FX pair which funds stand in.
|
|
195
195
|
const coinNote = underlying && isCoinWithoutOptions(underlying)
|
|
196
|
-
? `${optionsUnavailableTitle(parsePublicTickerKey(underlying).symbol)} ${COIN_OPTIONS_HINT}`
|
|
196
|
+
? `${optionsUnavailableTitle(parsePublicTickerKey(underlying).symbol)} ${COIN_OPTIONS_HINT}`
|
|
197
|
+
: underlying ? fxVolatilityNote(underlying, target?.effectiveExchange) : null;
|
|
197
198
|
const fateNote = useAsyncResource(underlying && noChain && !coinNote ? fateLoader : null).data ?? coinNote;
|
|
198
199
|
const ivRank = useIvRank(underlying || null);
|
|
199
200
|
// A coin's options (BTC-USD, ETH-USD) come with Deribit's volatility index.
|
|
@@ -224,9 +225,10 @@ export function VolSurfacePane({ focused, width, height }: PaneProps) {
|
|
|
224
225
|
// visible; the upstream chain cache dedupes the requests across users. A
|
|
225
226
|
// delayed surface keeps the configured refresh.
|
|
226
227
|
const realtimeSurface = !!settledSnapshot?.expiries.some((entry) => entry.dataSource === "live" || entry.realtimeEligible === true);
|
|
227
|
-
const
|
|
228
|
+
const liveRefresh = useLiveSessionRefresh(reloadLive, SURFACE_LIVE_RELOAD_MS,
|
|
228
229
|
!!symbol && spotAvailable && !historyDate && !fallbackDate && liveStreaming && realtimeSurface && !resource.loading,
|
|
229
230
|
resource.updatedAt);
|
|
231
|
+
const liveActive = liveRefresh.live;
|
|
230
232
|
useAutoRefresh(resource.updatedAt, liveActive ? noRefresh : resource.load);
|
|
231
233
|
const shownDate = historyDate ?? fallbackDate;
|
|
232
234
|
const storedLoader = useCallback(async () => storedSurfaceSnapshot(await loadStoredSurface(underlying, shownDate!)), [underlying, shownDate]);
|
|
@@ -396,7 +398,7 @@ export function VolSurfacePane({ focused, width, height }: PaneProps) {
|
|
|
396
398
|
...(pinned != null && snapshot && !resource.loading && !snapshot.catalogue.includes(pinned)
|
|
397
399
|
&& !snapshot.failures.some((failure) => failure.expiration === pinned)
|
|
398
400
|
? [`${expiryLabel(pinned)}: ${unlistedExpiryText(pinned, snapshot.catalogue)}`] : []),
|
|
399
|
-
...(active.error ? [active.error] : [])];
|
|
401
|
+
...(active.error ? [active.error] : []), ...(liveRefresh.error ? [liveRefresh.error] : [])];
|
|
400
402
|
const spotBasis = snapshot && !snapshot.stored ? surfaceSpotBasisLabel(snapshot) : null;
|
|
401
403
|
// What the quotes are and when they were observed; a reload moves the time.
|
|
402
404
|
const freshness = !snapshot ? null : snapshot.stored ? `stored close ${snapshot.stored.sessionDate}`
|
|
@@ -14,13 +14,13 @@ export const volatilityModule: PluginModule = {
|
|
|
14
14
|
paneTemplates: [{
|
|
15
15
|
id: "volatility-term-structure-pane", paneId: "volatility-term-structure",
|
|
16
16
|
label: "VIX Curve", description: "Dated VIX tenor closes, the 3M/30D ratio history and the cross-asset volatility board.",
|
|
17
|
-
keywords: ["vix", "volatility", "curve", "contango", "backwardation", "vvix", "skew", "move"],
|
|
17
|
+
keywords: ["vix", "volatility", "vol", "curve", "contango", "backwardation", "vvix", "skew", "move"],
|
|
18
18
|
shortcut: { prefix: "VIX" }, headless: volatilityHeadless,
|
|
19
19
|
createInstance: () => ({ instanceId: "volatility:curve", title: "VIX", placement: "floating", settings: { initialTab: "curve" } }),
|
|
20
20
|
}, {
|
|
21
21
|
id: "volatility-board-pane", paneId: "volatility-term-structure",
|
|
22
22
|
label: "Cross-asset Volatility", description: "Volatility index closes, daily changes and one-year percentiles.",
|
|
23
|
-
keywords: ["volatility", "cross asset", "vols", "vxn", "ovx", "gvz"],
|
|
23
|
+
keywords: ["volatility", "vol", "cross asset", "vols", "vxn", "ovx", "gvz"],
|
|
24
24
|
shortcut: { prefix: "VOLS" }, headless: volatilityHeadless,
|
|
25
25
|
createInstance: () => ({ instanceId: "volatility:board", title: "VOLS", placement: "floating", settings: { initialTab: "board" } }),
|
|
26
26
|
}],
|
|
@@ -107,7 +107,7 @@ export function VolatilityPane({ focused, width, height }: PaneProps) {
|
|
|
107
107
|
await coordinator.loadQuotesBatch(quiet.map((target) => ({ symbol: target.symbol, exchange: "" })), { forceRefresh: true });
|
|
108
108
|
}, [liveTargets]);
|
|
109
109
|
// With streaming off the quote layer already polls every minute.
|
|
110
|
-
useLiveSessionRefresh(refreshIndexLevels, INDEX_LEVEL_REFRESH_MS, liveStreaming && liveTargets.length > 0);
|
|
110
|
+
const levelRefresh = useLiveSessionRefresh(refreshIndexLevels, INDEX_LEVEL_REFRESH_MS, liveStreaming && liveTargets.length > 0);
|
|
111
111
|
const liveLevels = useThrottledValue(useMemo(() => {
|
|
112
112
|
const levels = new Map<VolatilityIndexId, VolatilityLiveLevel>();
|
|
113
113
|
for (const definition of VOLATILITY_INDICES) {
|
|
@@ -139,7 +139,7 @@ export function VolatilityPane({ focused, width, height }: PaneProps) {
|
|
|
139
139
|
}, [data?.board, sort, loading]);
|
|
140
140
|
const selected = rows.find((row) => row.id === selectedId) ?? rows[0] ?? null;
|
|
141
141
|
useVolatilityEvidence(result, tab, selected, resource.loading);
|
|
142
|
-
const notices = [resource.error, ...(result?.errors ?? []), ...(data?.warnings ?? []),
|
|
142
|
+
const notices = [resource.error, levelRefresh.error, ...(result?.errors ?? []), ...(data?.warnings ?? []),
|
|
143
143
|
...(selected?.warnings ?? [])].filter((notice): notice is string => !!notice);
|
|
144
144
|
usePaneNoticeFooter({ registrationId: "volatility-notices", notices, focused });
|
|
145
145
|
// From the tab the press finds, so the footer's hint never holds an old one.
|
|
@@ -171,8 +171,11 @@ export function VolatilityPane({ focused, width, height }: PaneProps) {
|
|
|
171
171
|
usePaneFooter("volatility", () => ({ info: [
|
|
172
172
|
...(resource.loading ? [{ id: "loading", parts: [{ text: "loading volatility", tone: "muted" as const }] }] : []),
|
|
173
173
|
...(result?.stale ? [{ id: "stale", parts: [{ text: "stale", tone: "warning" as const }] }] : []),
|
|
174
|
-
|
|
175
|
-
...(asOf ? [{ id: "
|
|
174
|
+
// One fact, "close 2026-09-25": the basis and its date read as one segment.
|
|
175
|
+
...(data || asOf ? [{ id: "basis", parts: [
|
|
176
|
+
...(data ? [{ text: observationBasis, tone: "muted" as const }] : []),
|
|
177
|
+
...(asOf ? [{ text: observationTime ? `${observationTime.slice(0, 16).replace("T", " ")} UTC` : asOf, tone: "muted" as const }] : []),
|
|
178
|
+
] }] : []),
|
|
176
179
|
], hints: [{ id: "view", key: "v", label: "iew", onPress: cycleTab }] }), [resource.loading, result?.stale, data, asOf, observationTime, observationBasis, cycleTab]);
|
|
177
180
|
const { strip: tabStrip, rows: tabRows } = usePaneTabs({ tabs: TABS, activeValue: tab, onSelect: setTab, focused, dense: true });
|
|
178
181
|
const contentHeight = Math.max(5, height - tabRows);
|
|
@@ -84,15 +84,15 @@ function layerColumns(layer: GeoLayerInfo): HeadlessPaneColumn[] {
|
|
|
84
84
|
/**
|
|
85
85
|
* What a search that finds nothing says. A search outside a one-country layer
|
|
86
86
|
* finds nothing because the layer has nothing there; ships are found by name,
|
|
87
|
-
*
|
|
88
|
-
*
|
|
87
|
+
* or by the name or ticker of an operator whose fleet is linked, so anything
|
|
88
|
+
* else (or a server that links no fleet) gets a name to try.
|
|
89
89
|
*/
|
|
90
90
|
function emptySearchNote(layer: GeoLayerInfo, query: string): string | null {
|
|
91
91
|
const typed = query.trim();
|
|
92
92
|
if (!typed) return null;
|
|
93
93
|
const coverage = layerCoverageNote(layer);
|
|
94
94
|
if (coverage) return `No ${layer.name.toLowerCase()} match "${typed}". ${coverage}`;
|
|
95
|
-
if (layer.id === "vessels") return `No ${layer.name.toLowerCase()} match "${typed}"; try a ship-name prefix, such as --query Norwegian.`;
|
|
95
|
+
if (layer.id === "vessels") return `No ${layer.name.toLowerCase()} match "${typed}"; try a ship-name prefix or an operator, such as --query Norwegian or --query "Royal Caribbean".`;
|
|
96
96
|
return null;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
2
|
import type { SwapCurvePayload, SwapTenor } from "../../../api-client/swap-curve";
|
|
3
|
-
import {
|
|
3
|
+
import { isPermanentClientError } from "../../../api-client/errors";
|
|
4
|
+
import { createPluginCache, type PluginCacheResult } from "../../../data/plugin-cache";
|
|
4
5
|
import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
5
6
|
import { validateSwapCurve } from "./swaps-model";
|
|
7
|
+
import { loadCurveData, type CurveData, type CurveId } from "./curves";
|
|
8
|
+
import { loadWorldRows, type WorldRow } from "./world";
|
|
6
9
|
|
|
7
10
|
export const swapCurveCache = createPluginCache<SwapCurvePayload>({
|
|
8
11
|
kind: "sofr-swap-curve", source: "gloom-cloud", schemaVersion: 1,
|
|
@@ -23,3 +26,60 @@ export async function fetchSwapCurve(tenor: SwapTenor,
|
|
|
23
26
|
export const getCachedSwapCurve = (tenor: SwapTenor) => cachedCloudResource(swapCurveCache, tenor, validateSwapCurve);
|
|
24
27
|
export const loadSwapCurve = (tenor: SwapTenor, force = false) =>
|
|
25
28
|
loadCloudResource(swapCurveCache, tenor, () => fetchSwapCurve(tenor), { force, validate: validateSwapCurve });
|
|
29
|
+
|
|
30
|
+
const DAY_MS = 24 * 60 * 60_000;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The Curve and World tabs' curves as last read, so a refresh that fails
|
|
34
|
+
* (offline, a service error) shows the copy an earlier read saved instead of
|
|
35
|
+
* an empty tab. Every load still asks the server; the copy is the fallback.
|
|
36
|
+
*/
|
|
37
|
+
export const curveViewCache = createPluginCache<CurveData>({
|
|
38
|
+
kind: "yield-curve-view", source: "gloom-cloud", schemaVersion: 1,
|
|
39
|
+
policy: { staleMs: 15 * 60_000, expireMs: 30 * DAY_MS },
|
|
40
|
+
});
|
|
41
|
+
export const worldCurvesCache = createPluginCache<WorldRow[]>({
|
|
42
|
+
kind: "world-curves", source: "gloom-cloud", schemaVersion: 1,
|
|
43
|
+
policy: { staleMs: 15 * 60_000, expireMs: 30 * DAY_MS },
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/** What a tab shows, and when the copy shown was saved when it is one, its refresh having failed. */
|
|
47
|
+
export interface SavedCopyAnswer<T> {
|
|
48
|
+
data: T;
|
|
49
|
+
savedAt: number | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const curveKey = (curve: CurveId, requestedDate: string) => `${curve}:${requestedDate || "latest"}`;
|
|
53
|
+
/**
|
|
54
|
+
* A copy is the saved one by where it came from, whatever its failure said. A
|
|
55
|
+
* refusal asking again will not change (access denied, an unknown curve) is
|
|
56
|
+
* said rather than covered by the copy, as `loadCloudResource` does.
|
|
57
|
+
*/
|
|
58
|
+
function answer<T>(result: PluginCacheResult<T>): SavedCopyAnswer<T> {
|
|
59
|
+
const saved = result.source === "stale-fallback";
|
|
60
|
+
if (saved && isPermanentClientError(result.error)) throw result.error;
|
|
61
|
+
return { data: result.data, savedAt: saved ? result.fetchedAt : null };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The curve on the date (its latest without one), or the copy an earlier read saved when it cannot be read. */
|
|
65
|
+
export async function loadSavedCurve(curve: CurveId, requestedDate: string,
|
|
66
|
+
client?: Parameters<typeof loadCurveData>[2]): Promise<SavedCopyAnswer<CurveData>> {
|
|
67
|
+
return answer(await curveViewCache.load(curveKey(curve, requestedDate), () => loadCurveData(curve, requestedDate, client), { force: true }));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The saved curve, for the tab's first paint while it is read again. */
|
|
71
|
+
export function cachedCurve(curve: CurveId, requestedDate: string): SavedCopyAnswer<CurveData> | null {
|
|
72
|
+
const cached = curveViewCache.get(curveKey(curve, requestedDate), { allowExpired: true });
|
|
73
|
+
return cached ? { data: cached.data, savedAt: null } : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Each market's latest curve, or the copy an earlier read saved when they cannot be read. */
|
|
77
|
+
export async function loadSavedWorldRows(client?: Parameters<typeof loadWorldRows>[0]): Promise<SavedCopyAnswer<WorldRow[]>> {
|
|
78
|
+
return answer(await worldCurvesCache.load("latest", () => loadWorldRows(client), { force: true }));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The saved world board, for the tab's first paint while it is read again. */
|
|
82
|
+
export function cachedWorldRows(): SavedCopyAnswer<WorldRow[]> | null {
|
|
83
|
+
const cached = worldCurvesCache.get("latest", { allowExpired: true });
|
|
84
|
+
return cached ? { data: cached.data, savedAt: null } : null;
|
|
85
|
+
}
|
|
@@ -4,16 +4,17 @@ import type {
|
|
|
4
4
|
} from "../../../types/plugin";
|
|
5
5
|
import {
|
|
6
6
|
compareDate, curveCutoff, CURVE_OPTIONS, curveIdOf, curveOption, curveSpreadFigures, curveTenorMove, loadComparePoints,
|
|
7
|
-
|
|
7
|
+
parseCompareInput, parseCurveArgument, type CurveData, type CurveId,
|
|
8
8
|
} from "./curves";
|
|
9
9
|
import { yieldTenorRows, type YieldLookbackCurves } from "./chart";
|
|
10
10
|
import { curveAsOf, type YieldPoint, yieldCurveErrors } from "./treasury-data";
|
|
11
11
|
import { loadYieldCurveLookbacks, yieldCurveDate } from "./history";
|
|
12
|
-
import {
|
|
12
|
+
import type { WorldRow } from "./world";
|
|
13
13
|
import { formatBasisPoints, toBasisPoints } from "../../../utils/basis-points";
|
|
14
14
|
import { DATE_OPTION_EXAMPLES } from "../../../utils/calendar-date";
|
|
15
15
|
import { SWAP_TENORS, type SwapCurvePayload, type SwapTenor } from "../../../api-client/swap-curve";
|
|
16
|
-
import { fetchSwapCurve } from "./client";
|
|
16
|
+
import { fetchSwapCurve, loadSavedCurve, loadSavedWorldRows } from "./client";
|
|
17
|
+
import { savedCopyMark } from "../../../data/saved-copies";
|
|
17
18
|
import { formatSwapBp, selectedSwapTenor } from "./swaps-model";
|
|
18
19
|
|
|
19
20
|
const percent = (value: unknown) => value == null ? "-" : `${Number(value).toFixed(2)}%`;
|
|
@@ -87,13 +88,14 @@ export interface YieldCurveHeadlessDependencies {
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
const defaultDependencies: Required<YieldCurveHeadlessDependencies> = {
|
|
90
|
-
|
|
91
|
+
// A curve that cannot be read is the copy an earlier read saved, marked so the report says so.
|
|
92
|
+
load: async (curve, requestedDate, context) => (await loadSavedCurve(curve, requestedDate, context.apiClient)).data,
|
|
91
93
|
compare: (curve, date, context) => loadComparePoints(curve, date, context.apiClient),
|
|
92
94
|
lookbacks: async (session, context) => Object.fromEntries(
|
|
93
95
|
(await loadYieldCurveLookbacks(session, (id, options) => context.apiClient.getCloudFredSeries(id, options)))
|
|
94
96
|
.map((lookback) => [lookback.id, lookback.points]),
|
|
95
97
|
),
|
|
96
|
-
world: (context) =>
|
|
98
|
+
world: async (context) => (await loadSavedWorldRows(context.apiClient)).data,
|
|
97
99
|
swaps: (tenor, context) => fetchSwapCurve(tenor, context.apiClient),
|
|
98
100
|
};
|
|
99
101
|
|
|
@@ -184,7 +186,7 @@ export function createYieldCurveHeadless(
|
|
|
184
186
|
return {
|
|
185
187
|
freshness: { source: "Government bond publishers", status: "not-a-feed", basis: "daily curves" },
|
|
186
188
|
columns: WORLD_COLUMNS,
|
|
187
|
-
rows: rows.map((row) => ({ market: row.market, twoYear: row.twoYear, tenYear: row.tenYear, thirtyYear: row.thirtyYear,
|
|
189
|
+
rows: rows.map((row) => ({ ...savedCopyMark(row), market: row.market, twoYear: row.twoYear, tenYear: row.tenYear, thirtyYear: row.thirtyYear,
|
|
188
190
|
twosTensBasisPoints: bp(row.twosTens), tenYearChangeBasisPoints: bp(row.tenYearChange), asOf: row.asOf })),
|
|
189
191
|
metadata: { units: "Percent" },
|
|
190
192
|
};
|
|
@@ -244,6 +246,7 @@ export function createYieldCurveHeadless(
|
|
|
244
246
|
const day = ranked ? curveTenorMove(data, point.maturity, "1D") : null;
|
|
245
247
|
const week = ranked ? curveTenorMove(data, point.maturity, "1W") : null;
|
|
246
248
|
return {
|
|
249
|
+
...savedCopyMark(data),
|
|
247
250
|
...point,
|
|
248
251
|
change1dBasisPoints: change == null ? null : toBasisPoints(change, 1),
|
|
249
252
|
...(ranked ? {
|
|
@@ -8,6 +8,7 @@ import { curveStrip, curveSurfaceMinRows } from "../../../components/chart/curve
|
|
|
8
8
|
import { curveLookbackLabel } from "../../../components/chart/curve/model";
|
|
9
9
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
10
10
|
import { useAsyncResource } from "../../../react/async-resource";
|
|
11
|
+
import { isPermanentClientError } from "../../../api-client/errors";
|
|
11
12
|
import { usePaneSettingValue } from "../../../state/app/context";
|
|
12
13
|
import { usePluginPaneState } from "../../../public/react";
|
|
13
14
|
import { priceColor } from "../../../theme/colors";
|
|
@@ -18,6 +19,7 @@ import type { PluginModule } from "../plugin-module";
|
|
|
18
19
|
import { GOVERNMENT_BOND_SERIES, TREASURY_SERIES } from "./series";
|
|
19
20
|
import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
20
21
|
import { usePaneStatusFooter } from "../../../components/layout/pane/status-footer";
|
|
22
|
+
import { savedCopyFooterSegment, type PaneFooterSegment } from "../../../components/layout/pane/footer";
|
|
21
23
|
import { formatPercentileRank } from "../../../utils/format";
|
|
22
24
|
import { SERIES_COLORS } from "../../../theme/series-colors";
|
|
23
25
|
import type { CurveSeries } from "../../../components/chart/curve";
|
|
@@ -27,20 +29,28 @@ import {
|
|
|
27
29
|
type YieldTenorRow,
|
|
28
30
|
} from "./chart";
|
|
29
31
|
import {
|
|
30
|
-
compareDate, curveCutoff, curveIdOf, CURVE_OPTIONS, curveSpreadFigures, curveTenorMove, loadComparePoints,
|
|
32
|
+
compareDate, curveCutoff, curveIdOf, CURVE_OPTIONS, curveSpreadFigures, curveTenorMove, loadComparePoints,
|
|
31
33
|
parseCompareInput, parseCurveArgument, type CurveId,
|
|
32
34
|
} from "./curves";
|
|
33
35
|
import { formatMoveRank, MOVE_HORIZONS } from "../shared/move-rank";
|
|
34
36
|
import { forwardCurve, type CurveBasis } from "./forward";
|
|
35
37
|
import { loadYieldCurveLookbacks, yieldCurveDate } from "./history";
|
|
36
38
|
import { curveAsOf, isYieldObservationDate, type YieldPoint, yieldCurveErrors } from "./treasury-data";
|
|
37
|
-
import {
|
|
39
|
+
import type { WorldRow } from "./world";
|
|
38
40
|
import { SwapsTab } from "./swaps";
|
|
39
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
cachedCurve, cachedWorldRows, curveViewCache, loadSavedCurve, loadSavedWorldRows, swapCurveCache, worldCurvesCache,
|
|
43
|
+
} from "./client";
|
|
40
44
|
|
|
41
45
|
type GcTab = "curve" | "world" | "swaps";
|
|
42
46
|
const TABS = [{ label: "Curve", value: "curve" }, { label: "World", value: "world" }, { label: "Swaps", value: "swaps" }];
|
|
43
47
|
const EMPTY_POINTS: YieldPoint[] = [];
|
|
48
|
+
|
|
49
|
+
/** A tab showing the copy an earlier read saved, its refresh having failed, says when that copy is from. */
|
|
50
|
+
function useSavedCopyInfo(savedAt: number | null | undefined): PaneFooterSegment[] | undefined {
|
|
51
|
+
return useMemo(() => savedAt != null ? [savedCopyFooterSegment("saved-copy", savedAt)] : undefined, [savedAt]);
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
/** Axis word for the loaded curve's basis. */
|
|
45
55
|
const BASIS_LABEL: Record<CurveBasis, string> = {
|
|
46
56
|
par: "Par yield",
|
|
@@ -165,8 +175,14 @@ function CurveTab({ curve, onCurveChange, focused, width, height }: {
|
|
|
165
175
|
const compareField = useDraftField(compareInput);
|
|
166
176
|
const editing = dateField.active || compareField.active;
|
|
167
177
|
|
|
168
|
-
|
|
169
|
-
|
|
178
|
+
// The saved copy paints first and answers when a refresh fails; a failed refresh never empties the tab, unless
|
|
179
|
+
// asking again cannot help (access denied), which is said instead.
|
|
180
|
+
const loadCurve = useCallback(async () => loadSavedCurve(curve, requestedDate), [curve, requestedDate]);
|
|
181
|
+
const { data: answer, loading, error, updatedAt: lastUpdated, load } = useAsyncResource(loadCurve, {
|
|
182
|
+
initialData: () => cachedCurve(curve, requestedDate), clearOnError: isPermanentClientError,
|
|
183
|
+
});
|
|
184
|
+
const data = answer?.data ?? null;
|
|
185
|
+
const savedInfo = useSavedCopyInfo(answer?.savedAt);
|
|
170
186
|
// A pending curve or date change must never relabel the previous curve as the new one.
|
|
171
187
|
const current = data?.curve === curve && data.requestedDate === requestedDate ? data : null;
|
|
172
188
|
const points = current?.points ?? EMPTY_POINTS;
|
|
@@ -260,6 +276,7 @@ function CurveTab({ curve, onCurveChange, focused, width, height }: {
|
|
|
260
276
|
registrationId: "yield-curve",
|
|
261
277
|
loading: loading || (compareKey != null && compareResource.loading && !comparePoints),
|
|
262
278
|
error: error || sourceError || null,
|
|
279
|
+
info: savedInfo,
|
|
263
280
|
hints: [
|
|
264
281
|
{ id: "date", key: "d", label: "ate", onPress: dateField.focus },
|
|
265
282
|
{ id: "compare", key: "v", label: "s", title: "Compare with a date", onPress: compareField.focus },
|
|
@@ -413,11 +430,14 @@ function WorldTab({ onOpen, focused, width, height }: {
|
|
|
413
430
|
}) {
|
|
414
431
|
const colors = useThemeColors();
|
|
415
432
|
const [selected, setSelected] = usePluginPaneState<string | null>("yield-curve:market", null);
|
|
416
|
-
const loadWorld = useCallback(() =>
|
|
417
|
-
const { data, loading, error, updatedAt, load } = useAsyncResource(loadWorld
|
|
433
|
+
const loadWorld = useCallback(() => loadSavedWorldRows(), []);
|
|
434
|
+
const { data: answer, loading, error, updatedAt, load } = useAsyncResource(loadWorld, {
|
|
435
|
+
initialData: cachedWorldRows, clearOnError: isPermanentClientError,
|
|
436
|
+
});
|
|
437
|
+
const data = answer?.data ?? null;
|
|
418
438
|
useAutoRefresh(updatedAt, load);
|
|
419
439
|
usePaneRefreshKey(() => void load(), { focused });
|
|
420
|
-
usePaneStatusFooter({ registrationId: "yield-curve:world", loading, error: error || null });
|
|
440
|
+
usePaneStatusFooter({ registrationId: "yield-curve:world", loading, error: error || null, info: useSavedCopyInfo(answer?.savedAt) });
|
|
421
441
|
const rows = data ?? [];
|
|
422
442
|
const selectedId = rows.some((row) => row.id === selected) ? selected! : rows[0]?.id ?? null;
|
|
423
443
|
// Every market's latest curve, each in its own colour; the selected row is
|
|
@@ -465,8 +485,12 @@ function WorldTab({ onOpen, focused, width, height }: {
|
|
|
465
485
|
}
|
|
466
486
|
|
|
467
487
|
export const yieldCurveModule: PluginModule = {
|
|
468
|
-
setup(ctx) {
|
|
469
|
-
|
|
488
|
+
setup(ctx) {
|
|
489
|
+
for (const cache of [swapCurveCache, curveViewCache, worldCurvesCache]) cache.attach(ctx.persistence);
|
|
490
|
+
},
|
|
491
|
+
dispose() {
|
|
492
|
+
for (const cache of [swapCurveCache, curveViewCache, worldCurvesCache]) cache.reset();
|
|
493
|
+
},
|
|
470
494
|
series: [...TREASURY_SERIES, ...GOVERNMENT_BOND_SERIES],
|
|
471
495
|
panes: [{
|
|
472
496
|
id: "yield-curve",
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
import { curveStrip, curveSurfaceMinRows } from "../../../components/chart/curve";
|
|
8
8
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
9
9
|
import { usePaneStatusFooter } from "../../../components/layout/pane/status-footer";
|
|
10
|
+
import { savedCopyFooterSegment } from "../../../components/layout/pane/footer";
|
|
10
11
|
import { FILL_REST } from "../../../components/ui/fill";
|
|
11
|
-
import {
|
|
12
|
+
import { isPermanentClientError } from "../../../api-client/errors";
|
|
12
13
|
import { usePluginPaneState } from "../../../public/react";
|
|
13
14
|
import { useAsyncResource } from "../../../react/async-resource";
|
|
14
15
|
import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
@@ -37,17 +38,20 @@ export function SwapsTab({ focused, width, height }: { focused: boolean; width:
|
|
|
37
38
|
const tenor = selectedSwapTenor(selected);
|
|
38
39
|
const loader = useCallback((force: boolean) => loadSwapCurve(tenor, force), [tenor]);
|
|
39
40
|
const resource = useAsyncResource(loader, {
|
|
40
|
-
initialData: () => getCachedSwapCurve(tenor), keepPreviousData: true, clearOnError:
|
|
41
|
+
initialData: () => getCachedSwapCurve(tenor), keepPreviousData: true, clearOnError: isPermanentClientError,
|
|
41
42
|
});
|
|
42
43
|
const payload = resource.data?.payload;
|
|
43
44
|
const rows = payload?.asOf ? payload.points : EMPTY_ROWS;
|
|
44
45
|
// A new tenor keeps the curve/table in place, but never relabels the old tenor's history.
|
|
45
46
|
const history = payload?.history.tenor === tenor ? payload.history : null;
|
|
46
|
-
|
|
47
|
-
const
|
|
47
|
+
// A refresh that failed shows the copy an earlier read saved, and says when that copy is from.
|
|
48
|
+
const savedAt = resource.data?.savedAt ?? null;
|
|
49
|
+
const savedInfo = useMemo(() => savedAt != null ? [savedCopyFooterSegment("saved-copy", savedAt)] : undefined, [savedAt]);
|
|
50
|
+
const stale = !!payload?.asOf && (payload.stale || (savedAt == null && resource.data?.stale) || false);
|
|
51
|
+
const error = resource.error || (savedAt == null ? resource.data?.refreshError : null) || null;
|
|
48
52
|
useAutoRefresh(stale ? null : resource.updatedAt, resource.load);
|
|
49
53
|
usePaneRefreshKey(resource.reload, { focused });
|
|
50
|
-
usePaneStatusFooter({ registrationId: "yield-curve:swaps", loading: resource.loading, error, stale });
|
|
54
|
+
usePaneStatusFooter({ registrationId: "yield-curve:swaps", loading: resource.loading, error, stale, info: savedInfo });
|
|
51
55
|
usePaneNoticeFooter({
|
|
52
56
|
registrationId: "yield-curve:swaps:notices", focused,
|
|
53
57
|
notices: payload?.asOf ? [
|
|
@@ -45,6 +45,7 @@ import { commentLettersPlugin } from "./builtin/comment-letters";
|
|
|
45
45
|
import { openFdaPlugin } from "./builtin/openfda";
|
|
46
46
|
import { browserGloomberbCloudPlugin } from "./builtin/cloud/browser";
|
|
47
47
|
import { changelogModule } from "./builtin/changelog";
|
|
48
|
+
import { roadmapModule } from "./builtin/roadmap";
|
|
48
49
|
import { chartComposerModule } from "./builtin/chart-composer";
|
|
49
50
|
import { connectionsModule } from "./builtin/connections";
|
|
50
51
|
import { relativeRotationModule } from "./builtin/relative-rotation";
|
|
@@ -126,7 +127,7 @@ import { FIXED_INCOME_HOSTS } from "./builtin/fixed-income/model";
|
|
|
126
127
|
|
|
127
128
|
const browserApplicationPlugin = composeBuiltinPlugin({
|
|
128
129
|
...applicationPluginMeta,
|
|
129
|
-
modules: [layoutManagerModule, helpModule, changelogModule, connectionsModule, docsModule],
|
|
130
|
+
modules: [layoutManagerModule, helpModule, changelogModule, connectionsModule, docsModule, roadmapModule],
|
|
130
131
|
});
|
|
131
132
|
|
|
132
133
|
const runtimeOnly = (module: PluginModule): PluginModule => {
|
|
@@ -75,7 +75,8 @@ const NATIVE_TARGETS: PluginTarget[] = ["cli", "tui", "desktop"];
|
|
|
75
75
|
*/
|
|
76
76
|
function functionsOf(plugin: GloomPlugin) {
|
|
77
77
|
return pluginFunctions({
|
|
78
|
-
|
|
78
|
+
// An unlisted function (`listed: false`) is not published.
|
|
79
|
+
templates: (plugin.paneTemplates ?? []).filter((template) => template.listed !== false).map((template) => ({
|
|
79
80
|
label: template.label,
|
|
80
81
|
prefix: template.shortcut?.prefix,
|
|
81
82
|
description: template.description,
|
package/src/plugins/host-link.ts
CHANGED
|
@@ -32,7 +32,7 @@ const LINKED_PACKAGES = ["gloomberb", "react"] as const;
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* The names the host is published under: `gloomberb`, which plugins import
|
|
35
|
-
* as, and `@gloom-sh/terminal
|
|
35
|
+
* as, and `@gloom-sh/terminal`.
|
|
36
36
|
*/
|
|
37
37
|
const HOST_PACKAGE_NAMES: ReadonlySet<string> = new Set(["gloomberb", "@gloom-sh/terminal"]);
|
|
38
38
|
|
|
@@ -186,6 +186,19 @@ function repoDirectory(repo: string | undefined): string | null {
|
|
|
186
186
|
return name ? name.toLowerCase() : null;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Built-ins write their copy as sentences and registry plugins as labels, so a
|
|
191
|
+
* row's short line is read without its closing period, whoever wrote it, and
|
|
192
|
+
* the detail's paragraph always has one. An ellipsis stays as written.
|
|
193
|
+
*/
|
|
194
|
+
function rowLine(text: string | undefined): string {
|
|
195
|
+
return (text ?? "").trim().replace(/(?<!\.)\.$/, "");
|
|
196
|
+
}
|
|
197
|
+
function detailParagraph(text: string | undefined): string | undefined {
|
|
198
|
+
const paragraph = text?.trim();
|
|
199
|
+
return paragraph ? /[\p{L}\p{N}]$/u.test(paragraph) ? `${paragraph}.` : paragraph : undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
189
202
|
/** The short line a built-in's row reads: its editorial tagline, else its description. */
|
|
190
203
|
function builtinTagline(local: InstalledPlugin): string {
|
|
191
204
|
return BUILTIN_EDITORIAL[local.id]?.tagline ?? local.description ?? "";
|
|
@@ -257,8 +270,8 @@ export function mergeCatalog(options: {
|
|
|
257
270
|
entries.push({
|
|
258
271
|
id: plugin.id,
|
|
259
272
|
name: builtin?.name ?? plugin.name,
|
|
260
|
-
tagline: builtin ? builtinTagline(builtin) : plugin.tagline,
|
|
261
|
-
description: builtin?.description ?? plugin.description,
|
|
273
|
+
tagline: rowLine(builtin ? builtinTagline(builtin) : plugin.tagline),
|
|
274
|
+
description: detailParagraph(builtin?.description ?? plugin.description),
|
|
262
275
|
categories: builtin ? BUILTIN_EDITORIAL[plugin.id]?.categories ?? plugin.categories : plugin.categories,
|
|
263
276
|
tier: plugin.tier,
|
|
264
277
|
targets: plugin.targets,
|
|
@@ -309,10 +322,10 @@ export function mergeCatalog(options: {
|
|
|
309
322
|
entries.push({
|
|
310
323
|
id: local.id,
|
|
311
324
|
name: local.name,
|
|
312
|
-
tagline: local.source === "builtin"
|
|
325
|
+
tagline: rowLine(local.source === "builtin"
|
|
313
326
|
? builtinTagline(local)
|
|
314
|
-
: local.description ?? (local.linked ? "Linked from a local checkout" : "Installed outside the registry"),
|
|
315
|
-
description: local.description,
|
|
327
|
+
: local.description ?? (local.linked ? "Linked from a local checkout" : "Installed outside the registry")),
|
|
328
|
+
description: detailParagraph(local.description),
|
|
316
329
|
categories: local.source === "builtin" ? [...BUILTIN_EDITORIAL[local.id]?.categories ?? []] : ["unlisted"],
|
|
317
330
|
// Shipped by Gloom like every built-in, whether or not the feed lists it.
|
|
318
331
|
tier: local.source === "builtin" ? "official" : "community",
|
|
@@ -185,15 +185,19 @@ function positionSideLabel(ticker: TickerRecord, activeTab: string | undefined):
|
|
|
185
185
|
return "MIX";
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
/**
|
|
188
|
+
/**
|
|
189
|
+
* The currency a row is listed in: its position's, else its quote's or the one saved with it. An index's level is in
|
|
190
|
+
* points and has none, and a row whose currency no source gave (a coin pair before its quote) shows none, never the
|
|
191
|
+
* dollars it is valued in meanwhile: a row not held has no position currency, only that fallback.
|
|
192
|
+
*/
|
|
189
193
|
function listedCurrency(
|
|
190
|
-
|
|
191
|
-
quoteCurrency: string,
|
|
194
|
+
metrics: Pick<PortfolioPositionMetrics, "positionCount" | "positionCurrency">,
|
|
192
195
|
ticker: TickerRecord,
|
|
193
196
|
quote: TickerFinancials["quote"],
|
|
194
197
|
): string {
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
const held = metrics.positionCount > 0 ? metrics.positionCurrency : "";
|
|
199
|
+
if (!held && isIndexLevel(quote?.instrumentType || ticker.metadata.assetCategory)) return "";
|
|
200
|
+
return (held || quote?.currency || ticker.metadata.currency || "").toUpperCase();
|
|
197
201
|
}
|
|
198
202
|
|
|
199
203
|
function compactText(value: string | null | undefined): string {
|
|
@@ -458,7 +462,7 @@ export function getColumnValue(
|
|
|
458
462
|
),
|
|
459
463
|
};
|
|
460
464
|
case "currency":
|
|
461
|
-
return { text: compactText(currencyCodeLabel(listedCurrency(
|
|
465
|
+
return { text: compactText(currencyCodeLabel(listedCurrency(positionMetrics, ticker, quote))) };
|
|
462
466
|
case "sector":
|
|
463
467
|
return { text: compactText(ticker.metadata.sector || financials?.profile?.sector) };
|
|
464
468
|
case "industry":
|
|
@@ -713,7 +717,7 @@ export function getSortValue(
|
|
|
713
717
|
?? ticker.metadata.exchange
|
|
714
718
|
?? null;
|
|
715
719
|
case "currency":
|
|
716
|
-
return listedCurrency(
|
|
720
|
+
return listedCurrency(positionMetrics, ticker, quote) || null;
|
|
717
721
|
case "sector":
|
|
718
722
|
return ticker.metadata.sector || financials?.profile?.sector || null;
|
|
719
723
|
case "industry":
|
|
@@ -36,10 +36,12 @@ export interface FuturesSettlement {
|
|
|
36
36
|
previous: number | null;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/**
|
|
39
|
+
/** A future's contract size, settlement and front reference for collection rows. */
|
|
40
40
|
export interface PortfolioFuturesTerms {
|
|
41
41
|
size: FuturesContractSize | null;
|
|
42
42
|
settlement: FuturesSettlement | null;
|
|
43
|
+
/** A different contract that is currently front, when the terms name one. */
|
|
44
|
+
front?: Pick<NonNullable<FuturesContractTermsPayload["front"]>, "contract" | "contractSymbol">;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
/** A grain or a soft quotes in US cents, so a dollar point value is a hundred of its price units. */
|
|
@@ -167,7 +169,7 @@ export function previousFuturesSettlement(
|
|
|
167
169
|
|
|
168
170
|
/** What a portfolio reads from a future's terms, with the settlement before the latest when the history has it. */
|
|
169
171
|
export function portfolioFuturesTerms(
|
|
170
|
-
terms: Pick<FuturesContractTermsPayload, "spec" | "settlement"> | null | undefined,
|
|
172
|
+
terms: (Pick<FuturesContractTermsPayload, "spec" | "settlement"> & Partial<Pick<FuturesContractTermsPayload, "front" | "contract">>) | null | undefined,
|
|
171
173
|
history?: readonly PricePoint[] | null,
|
|
172
174
|
): PortfolioFuturesTerms {
|
|
173
175
|
const settlement = terms?.settlement && Number.isFinite(terms.settlement.price) && terms.settlement.price > 0
|
|
@@ -177,7 +179,8 @@ export function portfolioFuturesTerms(
|
|
|
177
179
|
previous: previousFuturesSettlement(history, terms.settlement.date, terms.settlement.price),
|
|
178
180
|
}
|
|
179
181
|
: null;
|
|
180
|
-
|
|
182
|
+
const front = terms?.front && terms.front.contract !== terms.contract ? terms.front : undefined;
|
|
183
|
+
return { size: futuresContractSize(terms?.spec), settlement, ...(front ? { front } : {}) };
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
/**
|