gloomberb 0.18.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +169 -5
- package/src/account/auth-model.ts +7 -3
- package/src/api-client/auth.ts +9 -4
- package/src/api-client/catalysts.ts +2 -1
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +31 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +171 -11
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +13 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/request.ts +222 -41
- package/src/api-client/research-activity.ts +6 -12
- package/src/api-client/types.ts +74 -0
- package/src/app/keybindings/index.ts +3 -0
- package/src/app/keybindings/labels.ts +18 -1
- package/src/app/keybindings/resolve.ts +14 -0
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +42 -27
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +1 -1
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/fred-series.ts +5 -0
- package/src/cli/commands/keybindings.ts +23 -0
- package/src/cli/commands/market.ts +138 -46
- package/src/cli/commands/options-chain.ts +107 -10
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +176 -39
- package/src/cli/commands/search.ts +39 -3
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +57 -35
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +10 -2
- package/src/cli/export-source.ts +29 -1
- package/src/cli/glossary.ts +73 -1
- package/src/cli/history-rows.ts +87 -16
- package/src/cli/index.ts +2 -0
- package/src/cli/known-gaps.ts +5 -2
- package/src/cli/listing-arg.ts +195 -56
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +111 -16
- package/src/cli/pane-functions/function-help.ts +23 -13
- package/src/cli/pane-functions/headless.ts +56 -12
- package/src/cli/pane-functions/index.ts +58 -16
- package/src/cli/pane-functions/market-session.ts +81 -9
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/report.ts +5 -1
- package/src/cli/pane-functions/resolver.ts +35 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +30 -13
- package/src/cli/report-tables.ts +18 -7
- package/src/cli/restore-plugins.ts +13 -1
- package/src/cli/result.ts +57 -5
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +26 -4
- package/src/components/chart/composite/format.ts +27 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/levels.ts +2 -0
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +61 -5
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +46 -7
- package/src/components/chart/composite/series-axis-tags.tsx +16 -9
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/panel/prompt-binding.ts +0 -8
- package/src/components/command-bar/workflow/alert.ts +1 -1
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/header.tsx +26 -40
- package/src/components/layout/pane/footer/index.tsx +1 -0
- package/src/components/layout/pane/footer/model.ts +27 -0
- package/src/components/onboarding/onboarding-frame.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +4 -1
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/core/state/app/layout.ts +2 -0
- package/src/core/state/app/state.ts +9 -0
- package/src/core/state/app/types.ts +3 -0
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/saved-copies.ts +55 -16
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +427 -216
- package/src/i18n/es.ts +19 -3
- package/src/i18n/ja.ts +19 -3
- package/src/i18n/ko.ts +19 -3
- package/src/i18n/zh-cn.ts +19 -3
- package/src/i18n/zh-tw.ts +19 -3
- package/src/market-data/coordinator/financials.ts +10 -2
- package/src/market-data/coordinator/index.ts +3 -1
- package/src/market-data/coordinator/quotes.ts +4 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +48 -7
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/status.ts +16 -4
- package/src/market-data/options-alternatives.ts +141 -4
- package/src/market-data/quotes/contributions.ts +20 -2
- package/src/market-data/quotes/resolution.ts +3 -0
- package/src/market-data/selectors.ts +3 -0
- package/src/news/aggregator.ts +113 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/notification-log.ts +6 -4
- package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +380 -82
- package/src/plugins/builtin/alerts/command.ts +31 -0
- package/src/plugins/builtin/alerts/constants.ts +1 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- package/src/plugins/builtin/alerts/fired-log.ts +113 -8
- package/src/plugins/builtin/alerts/format.ts +80 -4
- package/src/plugins/builtin/alerts/headless.ts +30 -7
- package/src/plugins/builtin/alerts/history-rows.ts +7 -3
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +135 -22
- package/src/plugins/builtin/alerts/live.ts +3 -3
- package/src/plugins/builtin/alerts/metric-command.ts +134 -0
- package/src/plugins/builtin/alerts/metric-data.ts +111 -0
- package/src/plugins/builtin/alerts/metric-model.ts +324 -0
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
- package/src/plugins/builtin/alerts/pane.tsx +90 -39
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +37 -10
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/backtest/headless.ts +10 -5
- package/src/plugins/builtin/backtest/pane.tsx +9 -4
- package/src/plugins/builtin/backtest/presets.ts +29 -3
- package/src/plugins/builtin/broker-manager/index.tsx +10 -5
- package/src/plugins/builtin/broker-manager/model.ts +6 -1
- package/src/plugins/builtin/broker-trading/controller.ts +22 -12
- package/src/plugins/builtin/broker-trading/index.tsx +13 -1
- package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
- package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
- package/src/plugins/builtin/catalysts/model.ts +2 -1
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
- package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
- package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
- package/src/plugins/builtin/chart-composer/formula.ts +71 -18
- package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
- package/src/plugins/builtin/chart-composer/headless.ts +19 -3
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
- package/src/plugins/builtin/chart-composer/presets.ts +50 -15
- package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
- package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
- package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
- package/src/plugins/builtin/chat/content/composer.tsx +13 -6
- package/src/plugins/builtin/chat/content/footer.tsx +6 -3
- package/src/plugins/builtin/chat/content/index.tsx +50 -21
- package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
- package/src/plugins/builtin/chat/index.tsx +23 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
- package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
- package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -1
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +7 -7
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/calendar-model.ts +14 -9
- package/src/plugins/builtin/econ/figures.ts +72 -0
- package/src/plugins/builtin/econ/index.tsx +33 -3
- package/src/plugins/builtin/econ/types.ts +4 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
- package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
- package/src/plugins/builtin/econ-statistics/view.ts +3 -0
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
- package/src/plugins/builtin/exposure/charts.tsx +2 -2
- package/src/plugins/builtin/exposure/model.ts +13 -1
- package/src/plugins/builtin/exposure/pane.tsx +4 -4
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/futures/model.ts +2 -1
- package/src/plugins/builtin/futures/table.tsx +2 -2
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +11 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
- package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
- package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +36 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/pane.tsx +7 -5
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +25 -7
- package/src/plugins/builtin/market-movers/footer.ts +2 -2
- package/src/plugins/builtin/market-movers/headless.ts +32 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +14 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
- package/src/plugins/builtin/members/client.ts +19 -4
- package/src/plugins/builtin/members/headless.ts +20 -7
- package/src/plugins/builtin/members/index.tsx +9 -9
- package/src/plugins/builtin/members/model.ts +23 -4
- package/src/plugins/builtin/members/pane.tsx +25 -15
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +54 -6
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/model-spot.ts +5 -2
- package/src/plugins/builtin/options/strike-window.ts +2 -1
- package/src/plugins/builtin/options/table.ts +6 -9
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +42 -12
- package/src/plugins/builtin/options-positioning/client.ts +30 -0
- package/src/plugins/builtin/options-positioning/headless.ts +74 -21
- package/src/plugins/builtin/options-positioning/index.tsx +4 -4
- package/src/plugins/builtin/options-positioning/model.ts +122 -14
- package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/freshness.ts +12 -10
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/live-rows.ts +10 -3
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +32 -31
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
- package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/rate-path/pane.tsx +2 -2
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/scanner/feed.ts +2 -2
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +40 -6
- package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
- package/src/plugins/builtin/scanner/live-feed.ts +168 -0
- package/src/plugins/builtin/scanner/live-headless.ts +138 -0
- package/src/plugins/builtin/scanner/live-model.ts +362 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
- package/src/plugins/builtin/scanner/live-track.ts +186 -0
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +2 -1
- package/src/plugins/builtin/series-inventory.ts +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/macro-releases.ts +65 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +3 -2
- package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/headless.ts +6 -4
- package/src/plugins/builtin/themes/model.ts +17 -8
- package/src/plugins/builtin/themes/pane.tsx +7 -7
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
- package/src/plugins/builtin/vol-surface/client.ts +9 -17
- package/src/plugins/builtin/vol-surface/headless.ts +63 -14
- package/src/plugins/builtin/vol-surface/index.tsx +2 -2
- package/src/plugins/builtin/vol-surface/model.ts +22 -7
- package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
- package/src/plugins/builtin/vol-surface/tables.ts +31 -3
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +4 -4
- package/src/plugins/builtin/volatility/series.ts +26 -0
- package/src/plugins/builtin/world-indices/footer.ts +1 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-browser.ts +13 -8
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +5 -5
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/remote/resources.ts +2 -1
- package/src/renderers/browser/app-services.ts +1 -7
- package/src/renderers/browser/config-host.ts +2 -55
- package/src/renderers/browser/main.tsx +7 -57
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
- package/src/renderers/electrobun/bun/index.ts +25 -1
- package/src/renderers/electrobun/shared/app-name.ts +30 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/renderers/opentui/toast-host.tsx +71 -43
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
- package/src/sources/provider-router/batches.ts +10 -2
- package/src/sources/provider-router/financial-routes.ts +3 -3
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/history.ts +28 -7
- package/src/sources/provider-router/index.ts +149 -10
- package/src/sources/provider-router/search.ts +13 -2
- package/src/state/app/context/index.tsx +3 -0
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/auth-transition.ts +19 -9
- package/src/sync/baseline.ts +10 -4
- package/src/sync/controller.ts +70 -19
- package/src/sync/core-contributors.ts +90 -31
- package/src/sync/react.ts +4 -1
- package/src/sync/types.ts +3 -0
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/telemetry/usage-counts.ts +0 -18
- package/src/test-support/preload.ts +14 -0
- package/src/theme/colors.ts +8 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name.ts +103 -3
- package/src/tickers/listing-venues.ts +38 -3
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/index.ts +36 -7
- package/src/tickers/search/ranking.ts +10 -1
- package/src/tickers/search/types.ts +5 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/base-series.ts +13 -5
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +2 -4
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/forward-valuation.ts +32 -0
- package/src/time-series/fundamentals.ts +198 -7
- package/src/time-series/hooks.ts +1 -0
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/price-units.ts +72 -2
- package/src/time-series/resolve-context.ts +7 -1
- package/src/time-series/resolve.ts +116 -15
- package/src/time-series/series-presentation.ts +40 -6
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +5 -3
- package/src/time-series/studies.ts +278 -74
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +3 -3
- package/src/time-series/types.ts +27 -1
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/data-provider.ts +9 -1
- package/src/types/desktop-window.ts +2 -0
- package/src/types/financials.ts +73 -2
- package/src/types/headless.ts +61 -5
- package/src/types/instrument.ts +3 -0
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +10 -1
- package/src/ui/host.tsx +3 -0
- package/src/updater/command-names.ts +12 -7
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +44 -0
- package/src/utils/financial-statements.ts +6 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/network-failure.ts +9 -6
- package/src/utils/option-expiry.ts +48 -1
- package/src/utils/options.ts +10 -0
- package/src/utils/plugin-proxy-hosts.json +1 -0
- package/src/utils/price-history.ts +12 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
- package/src/renderers/browser/first-screen.ts +0 -193
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { CreditDocumentsPayload, CreditFact, CreditInstrument, CreditObligor, CreditScreenPayload } from "../../../api-client/credit-documents";
|
|
2
|
+
import type { HeadlessBundleSection, HeadlessPaneColumn, HeadlessPaneRow } from "../../../types/plugin";
|
|
3
|
+
import { capitalCell, capitalColumns, comparatorSymbol, covenantFact, covenantNumber, creditAmount, creditCurrencies, creditMaturityRows, creditPercent, factValue, fieldLabel,
|
|
4
|
+
instrumentOutstanding, isClosed, maturityShares, NO_VALUE, outstandingText, screenSignal, sentence } from "./model";
|
|
5
|
+
|
|
6
|
+
/** A column the text report leaves out: the exports keep it, for ids and figures a spreadsheet reads whole. */
|
|
7
|
+
type ExportColumn = HeadlessPaneColumn & { exportOnly?: boolean };
|
|
8
|
+
type Section = Extract<HeadlessBundleSection, { rows: HeadlessPaneRow[] }>;
|
|
9
|
+
|
|
10
|
+
const blank = (text: string) => text === NO_VALUE ? null : text;
|
|
11
|
+
const name = (obligor: CreditObligor | null | undefined) => obligor?.name ?? null;
|
|
12
|
+
const number = (value: unknown) => typeof value === "number" ? value : null;
|
|
13
|
+
const wide = (key: string, header: string, extra: Partial<HeadlessPaneColumn> = {}): ExportColumn => ({ key, header, shrink: true, minWidth: 16, ...extra });
|
|
14
|
+
const OBLIGOR: ExportColumn = { key: "obligor", header: "Obligor", minWidth: 16 };
|
|
15
|
+
const INSTRUMENT_ID: ExportColumn = { key: "instrumentId", header: "Instrument ID", stackPriority: 9 };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A table of flat records: the exports keep every column; the text report
|
|
19
|
+
* prints the pane's, and leaves out the obligor when the filing names none.
|
|
20
|
+
*/
|
|
21
|
+
function table(title: string, columns: readonly ExportColumn[], rows: HeadlessPaneRow[], { textKeys, empty }: { textKeys?: readonly string[]; empty?: string } = {}): Section {
|
|
22
|
+
const named = rows.some((row) => row.obligor != null);
|
|
23
|
+
const plain = (column: ExportColumn): HeadlessPaneColumn => { const { exportOnly: _, ...rest } = column; return rest; };
|
|
24
|
+
return { title, columns: columns.map(plain), rows,
|
|
25
|
+
textColumns: columns.filter((column) => !column.exportOnly && (column.key !== "obligor" || named) && (!textKeys || textKeys.includes(column.key))).map(plain),
|
|
26
|
+
...(empty ? { empty } : {}) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const CAPITAL: ExportColumn[] = [
|
|
30
|
+
{ key: "name", header: "Instrument", minWidth: 20, textFormat: (value, row) => isClosed({ lifecycle: row.lifecycle as string | null }) ? `${value} (${row.lifecycle})` : String(value) },
|
|
31
|
+
{ key: "lifecycle", header: "Status", exportOnly: true },
|
|
32
|
+
{ key: "facilityType", header: "Type", format: (value) => value ? fieldLabel(String(value)) : NO_VALUE },
|
|
33
|
+
{ key: "coupon", header: "Coupon/Margin" },
|
|
34
|
+
{ key: "maturity", header: "Maturity" },
|
|
35
|
+
{ key: "outstanding", header: "Outstanding", align: "right", textFormat: (value, row) => outstandingText(number(value), number(row.commitment)) },
|
|
36
|
+
{ key: "commitment", header: "Commitment", align: "right", exportOnly: true },
|
|
37
|
+
{ key: "currency", header: "CCY" },
|
|
38
|
+
{ key: "ranking", header: "Rank", minWidth: 12 },
|
|
39
|
+
{ key: "controlPut", header: "CoC put", align: "right" },
|
|
40
|
+
OBLIGOR,
|
|
41
|
+
INSTRUMENT_ID,
|
|
42
|
+
];
|
|
43
|
+
/** The pane's capital column behind each export column; the others are not in the pane's own list. */
|
|
44
|
+
const PANE_COLUMN: Record<string, string> = { name: "name", facilityType: "facility", coupon: "coupon", maturity: "maturity", outstanding: "principal", currency: "currency", ranking: "ranking", controlPut: "control" };
|
|
45
|
+
|
|
46
|
+
function capitalRows(data: CreditDocumentsPayload, instruments: readonly CreditInstrument[]): HeadlessPaneRow[] {
|
|
47
|
+
return instruments.map((row) => ({
|
|
48
|
+
name: row.name, lifecycle: row.lifecycle ?? null, facilityType: row.facilityType,
|
|
49
|
+
coupon: blank(capitalCell(row, "coupon", data)), maturity: row.maturity, outstanding: instrumentOutstanding(row), commitment: row.commitment,
|
|
50
|
+
currency: row.currency, ranking: blank(capitalCell(row, "ranking", data)), controlPut: blank(capitalCell(row, "control", data)),
|
|
51
|
+
obligor: name(row.obligor), instrumentId: row.id,
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
/** The instruments as the Capital tab lists them; a column the pane leaves out as undisclosed stays out of the text report. */
|
|
55
|
+
export function capitalSection(data: CreditDocumentsPayload, title = "Capital structure", instruments: readonly CreditInstrument[] = data.instruments): Section {
|
|
56
|
+
const shown = new Set(capitalColumns(data, Number.MAX_SAFE_INTEGER).map((column) => column.id));
|
|
57
|
+
return table(title, CAPITAL, capitalRows(data, instruments), { textKeys: CAPITAL.map((column) => column.key).filter((key) => !(key in PANE_COLUMN) || shown.has(PANE_COLUMN[key]!)) });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The unit a covenant row's figures are in, read from the fact its columns carry. */
|
|
61
|
+
const figureUnit = (row: HeadlessPaneRow) => ({ unit: typeof row.unit === "string" ? row.unit : null, currency: typeof row.currency === "string" ? row.currency : null });
|
|
62
|
+
const COVENANTS: ExportColumn[] = [
|
|
63
|
+
wide("metric", "Covenant"),
|
|
64
|
+
{ key: "threshold", header: "Limit", align: "right", textFormat: (value, row) => `${comparatorSymbol(row.comparator as string, row.inclusive as boolean | null ?? undefined)} ${covenantNumber(number(value), figureUnit(row))}` },
|
|
65
|
+
{ key: "comparator", header: "Comparator", exportOnly: true },
|
|
66
|
+
{ key: "inclusive", header: "Inclusive", exportOnly: true },
|
|
67
|
+
{ key: "unit", header: "Unit", exportOnly: true },
|
|
68
|
+
{ key: "currency", header: "CCY", exportOnly: true },
|
|
69
|
+
{ key: "current", header: "Reported", align: "right", textFormat: (value, row) => covenantNumber(number(value), figureUnit(row)) },
|
|
70
|
+
{ key: "headroomPercent", header: "Headroom", align: "right", format: (value) => creditPercent(number(value)) },
|
|
71
|
+
{ key: "status", header: "Status", format: (value) => fieldLabel(String(value)) },
|
|
72
|
+
{ key: "asOf", header: "As of" },
|
|
73
|
+
{ key: "testDate", header: "Test date", exportOnly: true },
|
|
74
|
+
wide("instrument", "Instrument"),
|
|
75
|
+
OBLIGOR,
|
|
76
|
+
{ key: "reason", header: "Reason", underRow: true },
|
|
77
|
+
{ key: "id", header: "Covenant ID", exportOnly: true },
|
|
78
|
+
{ key: "instrumentId", header: "Instrument ID", exportOnly: true },
|
|
79
|
+
];
|
|
80
|
+
export function covenantSection(data: CreditDocumentsPayload): Section {
|
|
81
|
+
return table("Covenants", COVENANTS, data.covenants.map((row) => {
|
|
82
|
+
const fact = covenantFact(data, row);
|
|
83
|
+
return { metric: sentence(row.metric), threshold: row.threshold, comparator: row.comparator, inclusive: row.inclusive ?? null, unit: fact?.unit ?? null, currency: fact?.currency ?? null,
|
|
84
|
+
current: row.current, headroomPercent: row.headroomPercent, status: row.status, asOf: row.asOf, testDate: row.testDate, instrument: row.instrumentName,
|
|
85
|
+
obligor: name(row.obligor), reason: row.reason, id: row.id, instrumentId: row.instrumentId };
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function maturityColumns(preview: boolean): ExportColumn[] {
|
|
90
|
+
return [
|
|
91
|
+
{ key: "year", header: "Year" },
|
|
92
|
+
{ key: "currency", header: "CCY" },
|
|
93
|
+
{ key: "principal", header: "Principal", align: "right", textFormat: (value) => creditAmount(number(value)) },
|
|
94
|
+
...preview ? [] : [
|
|
95
|
+
{ key: "share", header: "Share", align: "right", format: (value: unknown) => creditPercent(number(value)) } as ExportColumn,
|
|
96
|
+
{ key: "cumulative", header: "Cumulative", align: "right", format: (value: unknown) => creditPercent(number(value)) } as ExportColumn,
|
|
97
|
+
],
|
|
98
|
+
wide("instrument", "Instrument"),
|
|
99
|
+
OBLIGOR,
|
|
100
|
+
INSTRUMENT_ID,
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
/** One row per instrument and year, in each currency: wall currencies are never added together. A preview wall is partial, so it has no shares. */
|
|
104
|
+
export function maturitySection(data: CreditDocumentsPayload): Section {
|
|
105
|
+
const preview = data.access === "preview";
|
|
106
|
+
const rows = creditCurrencies(data).flatMap((currency) => {
|
|
107
|
+
const buckets = creditMaturityRows(data, currency), shares = preview ? new Map() : maturityShares(buckets);
|
|
108
|
+
return buckets.flatMap((bucket) => bucket.instruments.map((instrument) => {
|
|
109
|
+
const share = shares.get(`${bucket.year}:${instrument.id}`);
|
|
110
|
+
return { year: bucket.year, currency, principal: instrument.principal, ...preview ? {} : { share: share?.share ?? null, cumulative: share?.cumulative ?? null },
|
|
111
|
+
instrument: instrument.name, obligor: name(bucket.obligor), instrumentId: instrument.id };
|
|
112
|
+
}));
|
|
113
|
+
});
|
|
114
|
+
return table("Maturity wall", maturityColumns(preview), rows);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const CONTROL: ExportColumn[] = [
|
|
118
|
+
wide("instrument", "Instrument", { minWidth: 20 }),
|
|
119
|
+
{ key: "putPercent", header: "Put", align: "right", format: (value) => value == null ? "" : `${value}%` },
|
|
120
|
+
{ key: "principal", header: "Principal", align: "right", textFormat: (value) => creditAmount(number(value)) },
|
|
121
|
+
{ key: "currency", header: "CCY" },
|
|
122
|
+
wide("trigger", "Trigger", { minWidth: 24 }),
|
|
123
|
+
OBLIGOR,
|
|
124
|
+
INSTRUMENT_ID,
|
|
125
|
+
];
|
|
126
|
+
export function controlSection(data: CreditDocumentsPayload): Section {
|
|
127
|
+
return table("Change of control", CONTROL, data.changeOfControl.map((row) => ({ instrument: row.instrumentName, putPercent: row.putPercent, principal: row.principal,
|
|
128
|
+
currency: row.currency, trigger: row.trigger, obligor: name(row.obligor), instrumentId: row.instrumentId })));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const SCREEN: ExportColumn[] = [
|
|
132
|
+
{ key: "symbol", header: "Issuer" },
|
|
133
|
+
{ key: "signal", header: "Signal" },
|
|
134
|
+
{ key: "headroomPercent", header: "Headroom", align: "right", format: (value) => creditPercent(number(value)) },
|
|
135
|
+
{ key: "date", header: "Date" },
|
|
136
|
+
wide("instrument", "Instrument", { minWidth: 20 }),
|
|
137
|
+
wide("condition", "Condition", { minWidth: 24 }),
|
|
138
|
+
{ key: "currency", header: "CCY", exportOnly: true },
|
|
139
|
+
INSTRUMENT_ID,
|
|
140
|
+
];
|
|
141
|
+
/** The risk screen's signals as the Screen tab lists them; the evidence behind each stays behind `--instrument`. */
|
|
142
|
+
export function screenSection(data: CreditScreenPayload): Section {
|
|
143
|
+
return table("Credit risk screen", SCREEN, data.rows.map((row) => ({ symbol: row.symbol, signal: screenSignal(row), headroomPercent: row.value, date: row.date, instrument: row.instrumentName,
|
|
144
|
+
condition: sentence(row.reason), currency: row.currency, instrumentId: row.instrumentId })));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const FACTS: ExportColumn[] = [
|
|
148
|
+
wide("term", "Term", { shrink: false }),
|
|
149
|
+
wide("value", "Disclosed value", { minWidth: 24 }),
|
|
150
|
+
{ key: "amount", header: "Amount", exportOnly: true, align: "right" },
|
|
151
|
+
{ key: "unit", header: "Unit", exportOnly: true },
|
|
152
|
+
{ key: "asOf", header: "As of" },
|
|
153
|
+
{ key: "filedAt", header: "Filed" },
|
|
154
|
+
{ key: "status", header: "Revision", format: (value) => fieldLabel(String(value)) },
|
|
155
|
+
{ key: "form", header: "Form", exportOnly: true },
|
|
156
|
+
{ key: "confidence", header: "Confidence", exportOnly: true, align: "right" },
|
|
157
|
+
{ key: "filingUrl", header: "Filing URL", exportOnly: true },
|
|
158
|
+
{ key: "id", header: "Fact ID", exportOnly: true },
|
|
159
|
+
// The filing's own words belong to the instrument view; the text report prints them under their term.
|
|
160
|
+
{ key: "quote", header: "Filing text", underRow: true },
|
|
161
|
+
];
|
|
162
|
+
const factRow = (fact: CreditFact): HeadlessPaneRow => ({ term: fieldLabel(fact.field), value: factValue(fact), amount: number(fact.value), unit: fact.currency ?? fact.unit, asOf: fact.asOf, filedAt: fact.filedAt, status: fact.status,
|
|
163
|
+
form: fact.form, confidence: fact.confidence, filingUrl: fact.filingUrl, id: fact.id, quote: fact.quote });
|
|
164
|
+
/** An instrument's terms and amendments, each with the filing text behind it. Current terms are all in effect, so only the history names the revision. */
|
|
165
|
+
export function instrumentSections(data: CreditDocumentsPayload, instrument: CreditInstrument): Section[] {
|
|
166
|
+
const byTerm = (a: CreditFact, b: CreditFact) => a.field.localeCompare(b.field) || a.factKey.localeCompare(b.factKey);
|
|
167
|
+
const history = [...instrument.history ?? []].sort((a, b) => b.filedAt.localeCompare(a.filedAt) || a.field.localeCompare(b.field));
|
|
168
|
+
return [
|
|
169
|
+
capitalSection(data, instrument.name, [instrument]),
|
|
170
|
+
table("Current terms", FACTS, [...instrument.facts].sort(byTerm).map(factRow), { textKeys: FACTS.map((column) => column.key).filter((key) => key !== "status") }),
|
|
171
|
+
table("Amendment history", FACTS, history.map(factRow)),
|
|
172
|
+
];
|
|
173
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
2
|
import type { CreditDocumentsPayload } from "../../../api-client/credit-documents";
|
|
3
3
|
import { creditIssuerSymbol, fetchCreditDocuments, fetchCreditInstrument, fetchCreditScreen } from "./client";
|
|
4
|
-
import {
|
|
4
|
+
import { capitalSection, controlSection, covenantSection, instrumentSections, maturitySection, screenSection } from "./export";
|
|
5
|
+
import { CREDIT_TABS, creditEmptyCause } from "./model";
|
|
5
6
|
import { SEC_FILINGS } from "../shared/report-freshness";
|
|
6
7
|
|
|
7
8
|
export const creditHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
@@ -25,7 +26,7 @@ export const creditHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
25
26
|
if (args.options.tab === "screen") {
|
|
26
27
|
const data = await fetchCreditScreen({ headroomBelow: Number(args.options.headroom ?? 20), springingWithinMonths: Number(args.options.months ?? 12), limit: 100 }, ctx.apiClient);
|
|
27
28
|
return { complete: data.lockedRows === 0 && !data.truncated, errors: [...(data.lockedRows ? ["More results require Gloom Pro"] : []), ...(data.truncated ? ["Screen reached the result limit"] : [])],
|
|
28
|
-
sections: [
|
|
29
|
+
sections: [screenSection(data)], metadata: { asOf: data.asOf, access: data.access, lockedRows: data.lockedRows } };
|
|
29
30
|
}
|
|
30
31
|
const data = await fetchCreditDocuments(args.symbols[0]!, ctx.apiClient);
|
|
31
32
|
const instrument = args.options.instrument ? await fetchCreditInstrument(data.symbol, String(args.options.instrument), ctx.apiClient) : null;
|
|
@@ -36,8 +37,7 @@ export const creditHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
36
37
|
metadata: { symbol: data.symbol, issuer: data.issuer, asOf: data.asOf, access: data.access, lockedRows: data.lockedRows, status: data.status, reason, notices: [reason] } };
|
|
37
38
|
}
|
|
38
39
|
return { complete: data.status === "available" && data.lockedRows === 0, errors: [...data.warnings, ...(data.lockedRows ? ["Full credit documents and history require Gloom Pro"] : [])],
|
|
39
|
-
sections: instrument ?
|
|
40
|
-
: [{ title: "Capital structure", rows: data.instruments.map((row) => ({ ...row })) }, { title: "Covenants", rows: data.covenants.map((row) => ({ ...row })) }, { title: "Maturity wall", rows: data.maturities.map((row) => ({ ...row })) }, { title: "Change of control", rows: data.changeOfControl.map((row) => ({ ...row })) }],
|
|
40
|
+
sections: instrument ? instrumentSections(data, instrument) : [capitalSection(data), covenantSection(data), maturitySection(data), controlSection(data)],
|
|
41
41
|
metadata: { symbol: data.symbol, issuer: data.issuer, asOf: data.asOf, access: data.access, lockedRows: data.lockedRows, status: data.status } };
|
|
42
42
|
},
|
|
43
43
|
};
|
|
@@ -55,10 +55,10 @@ export function creditEmptyReason(
|
|
|
55
55
|
data: Pick<CreditDocumentsPayload, "symbol" | "issuer" | "instruments" | "covenants" | "maturities" | "changeOfControl" | "lockedRows">,
|
|
56
56
|
requested: string,
|
|
57
57
|
): string | null {
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
58
|
+
const cause = creditEmptyCause(data);
|
|
59
|
+
if (!cause) return null;
|
|
60
60
|
const symbol = requested.trim().toUpperCase();
|
|
61
|
-
const why = data.issuer
|
|
61
|
+
const why = cause === "instruments" && data.issuer
|
|
62
62
|
? `No credit documents on file for ${data.issuer.name} (${symbol}): no debt instrument has been read from its filings yet.`
|
|
63
63
|
: `No credit documents on file for ${symbol}: it is not an issuer the credit-document set covers.`;
|
|
64
64
|
const example = creditIssuerSymbol(symbol) === CREDIT_EXAMPLE_SYMBOL ? "" : ` Try \`gloom fn CRDOC ${CREDIT_EXAMPLE_SYMBOL}\` for an issuer with documents.`;
|
|
@@ -54,12 +54,14 @@ function structuredSummary(fact: CreditFact): string | null {
|
|
|
54
54
|
const low = finite(value.minimum), high = finite(value.maximum);
|
|
55
55
|
return low !== null && high !== null ? [`${low}–${high}${unitSuffix(fact.unit)}`, words(value.condition)].filter(Boolean).join(" · ") : null;
|
|
56
56
|
}
|
|
57
|
+
const VALUE_KINDS = new Set(["text", "date"]);
|
|
57
58
|
export function factValue(fact: CreditFact): string {
|
|
58
59
|
if (typeof fact.value === "number" && ["percent", "%"].includes(fact.unit ?? "")) return `${fact.value}%`;
|
|
59
60
|
const summary = structuredSummary(fact);
|
|
60
61
|
if (summary) return summary;
|
|
61
62
|
const value = typeof fact.value === "number" && fact.currency ? creditAmount(fact.value) : typeof fact.value === "string" ? sentence(fact.value) : creditValue(fact.value);
|
|
62
|
-
|
|
63
|
+
// `text` and `date` say what kind of value a fact holds, not what it is measured in.
|
|
64
|
+
return [value, fact.currency ?? (fact.unit && !VALUE_KINDS.has(fact.unit) ? fact.unit : null)].filter(Boolean).join(" ");
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
export interface CreditTermField { label: string; value: string; detail?: string }
|
|
@@ -98,13 +100,34 @@ function instrumentFact(row: CreditInstrument, field: string): CreditFact | unde
|
|
|
98
100
|
return row.facts.find((fact) => fact.field === field);
|
|
99
101
|
}
|
|
100
102
|
const CLOSED = new Set(["repaid", "redeemed", "terminated", "cancelled", "matured"]);
|
|
101
|
-
export const isClosed = (row: CreditInstrument) => !!row.lifecycle && CLOSED.has(row.lifecycle);
|
|
103
|
+
export const isClosed = (row: Pick<CreditInstrument, "lifecycle">) => !!row.lifecycle && CLOSED.has(row.lifecycle);
|
|
102
104
|
/** What is owed now: a revolver's drawn balance, otherwise outstanding principal. */
|
|
103
105
|
export const instrumentOutstanding = (row: CreditInstrument) => row.drawn ?? row.principal;
|
|
104
|
-
|
|
106
|
+
/** A margin in basis points, from the unit the filing states it in; null when the unit does not say. */
|
|
107
|
+
function marginBasisPoints(margin: CreditFact): number | null {
|
|
108
|
+
const value = finite(margin.value);
|
|
109
|
+
if (value === null) return null;
|
|
110
|
+
if (margin.unit === "bps" || margin.unit === "bp") return value;
|
|
111
|
+
return margin.unit === "percent" || margin.unit === "%" ? Number((value * 100).toFixed(2)) : null;
|
|
112
|
+
}
|
|
113
|
+
/** SOFR is written `S`, as a floating spread is quoted; any other named base keeps its name. */
|
|
114
|
+
const baseLabel = (reference: string) => /\bSOFR\b/i.test(reference) ? "S" : reference;
|
|
115
|
+
/**
|
|
116
|
+
* What an instrument pays: a fixed coupon as its percentage, a floating margin
|
|
117
|
+
* as its spread over the base in basis points (`S+148`, `EURIBOR+175`). A
|
|
118
|
+
* margin with no named base is read as SOFR for a dollar or undated currency
|
|
119
|
+
* instrument, the market's base for dollar loans, and is left bare for any other.
|
|
120
|
+
*/
|
|
121
|
+
export function creditCoupon(row: CreditInstrument): string {
|
|
105
122
|
const coupon = instrumentFact(row, "coupon"), margin = instrumentFact(row, "margin"), reference = instrumentFact(row, "reference_rate");
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
const named = reference ? words(typeof reference.value === "string" ? reference.value : creditValue(reference.value)) : null;
|
|
124
|
+
if (margin) {
|
|
125
|
+
const base = named ? baseLabel(named) : !row.currency || row.currency === "USD" ? "S" : "";
|
|
126
|
+
const basisPoints = marginBasisPoints(margin);
|
|
127
|
+
return basisPoints === null ? `${base}+${factValue(margin)}` : `${base}${basisPoints < 0 ? "-" : "+"}${Math.abs(basisPoints)}`;
|
|
128
|
+
}
|
|
129
|
+
return coupon ? factValue(coupon) : named ?? NO_VALUE;
|
|
130
|
+
}
|
|
108
131
|
const rankText = (row: CreditInstrument) => {
|
|
109
132
|
const fact = instrumentFact(row, "ranking") ?? instrumentFact(row, "security");
|
|
110
133
|
return fact ? sentence(creditValue(fact.value)) : NO_VALUE;
|
|
@@ -114,6 +137,74 @@ function securityClass(row: CreditInstrument): "secured" | "unsecured" | null {
|
|
|
114
137
|
const text = ["ranking", "security"].map((field) => { const fact = instrumentFact(row, field); return fact ? creditValue(fact.value).toLowerCase() : ""; }).join(" ");
|
|
115
138
|
return /\bunsecured\b/.test(text) ? "unsecured" : /\bsecured\b/.test(text) ? "secured" : null;
|
|
116
139
|
}
|
|
140
|
+
const reasonKey = (text: string) => text.trim().replace(/\.$/, "").toLowerCase();
|
|
141
|
+
/**
|
|
142
|
+
* The phrases the headroom check gives for a covenant it cannot compute, in plain
|
|
143
|
+
* words. The payload carries the phrase and no code, so each is matched whole.
|
|
144
|
+
* An empty line says nothing more than the missing figure, which comes first.
|
|
145
|
+
*/
|
|
146
|
+
const PLAIN_REASONS: Record<string, string | ((limit: string) => string)> = {
|
|
147
|
+
"covenant boundary inclusivity is not established": (limit) => `The filing doesn't say if exactly ${limit} passes.`,
|
|
148
|
+
"springing test; activation conditions require review": "Only tested once a trigger is met; the filing's trigger needs a read.",
|
|
149
|
+
"covenant adjustments are not fully disclosed": "The filing does not spell out how the ratio is adjusted.",
|
|
150
|
+
"no reported value for this covenant definition": "",
|
|
151
|
+
"reported metric units or currency do not match the covenant": "The reported figure is in other units than the limit.",
|
|
152
|
+
"reported metric definition or borrower scope does not match the covenant": "The reported figure is defined differently from the covenant's ratio.",
|
|
153
|
+
"latest comparable financials are more than 190 days old": "The latest comparable financials are over 190 days old.",
|
|
154
|
+
"financial period is in the future": "The comparable financials are dated in the future.",
|
|
155
|
+
"financial period does not match the covenant test date": "The reported period is not the covenant's test date.",
|
|
156
|
+
"a positive threshold is required to express percentage headroom": "Percentage headroom needs a limit above zero.",
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Why a covenant has no headroom, in a few plain words, or null when it has a
|
|
160
|
+
* figure or no reason. The missing reported figure comes first when the
|
|
161
|
+
* instrument's facts hold none for this covenant's metric; then what the
|
|
162
|
+
* covenant's own terms or the check say. A step-up in the filing's adjustment
|
|
163
|
+
* text is named with its limit. A phrase not known here is the payload's own,
|
|
164
|
+
* as it came.
|
|
165
|
+
*/
|
|
166
|
+
export function covenantReason(row: Pick<CreditHeadroom, "id" | "status" | "reason" | "metric" | "threshold">, facts: readonly CreditFact[]): string | null {
|
|
167
|
+
const raw = row.status === "compliant" || row.status === "breach" ? null : row.reason?.trim();
|
|
168
|
+
if (!raw) return null;
|
|
169
|
+
const fact = facts.find((entry) => entry.id === row.id);
|
|
170
|
+
const limit = covenantNumber(row.threshold, fact);
|
|
171
|
+
const known = PLAIN_REASONS[reasonKey(raw)];
|
|
172
|
+
const reported = facts.some((entry) => entry.field === "reported_metric" && reasonKey(reportedMetricName(entry)) === reasonKey(row.metric));
|
|
173
|
+
// Said only with the instrument's facts in hand: with none loaded, there is nothing to show it has no figure.
|
|
174
|
+
const lead = known === "" || (facts.length > 0 && !reported) ? `No reported ${row.metric.trim()} to compare yet.` : null;
|
|
175
|
+
const stepUp = /step[- ]?up to ([\d.]+)/i.exec(raw);
|
|
176
|
+
let why: string | null;
|
|
177
|
+
if (typeof known === "function") why = known(limit);
|
|
178
|
+
else if (known !== undefined) why = known || null;
|
|
179
|
+
else if (stepUp) why = `The ${limit} limit can step up to ${covenantNumber(Number(stepUp[1]), fact)}${/acquisition/i.test(raw) ? " after permitted acquisitions" : " under conditions in the filing"}.`;
|
|
180
|
+
else why = `${sentence(raw)}${/[.!?]$/.test(raw) ? "" : "."}`;
|
|
181
|
+
return [lead, why].filter(Boolean).join(" ");
|
|
182
|
+
}
|
|
183
|
+
/** The metric a reported-figure fact is for, from its structured value. */
|
|
184
|
+
function reportedMetricName(fact: CreditFact): string {
|
|
185
|
+
return words(fields(fact.value)?.metric) ?? "";
|
|
186
|
+
}
|
|
187
|
+
/** What an issuer with no rows at all lacks: the issuer itself, or a debt instrument read from its filings. */
|
|
188
|
+
type CreditEmptyCause = "issuer" | "instruments";
|
|
189
|
+
/**
|
|
190
|
+
* Why a payload has nothing to show in any section. Null when a section has a
|
|
191
|
+
* row, or Pro holds some back: a preview that is partly locked is not empty.
|
|
192
|
+
*/
|
|
193
|
+
export function creditEmptyCause(data: Pick<CreditDocumentsPayload, "issuer" | "instruments" | "covenants" | "maturities" | "changeOfControl" | "lockedRows">): CreditEmptyCause | null {
|
|
194
|
+
const rows = data.instruments.length + data.covenants.length + data.maturities.length + data.changeOfControl.length;
|
|
195
|
+
return rows > 0 || data.lockedRows > 0 ? null : data.issuer ? "instruments" : "issuer";
|
|
196
|
+
}
|
|
197
|
+
/** The pane's line for an empty payload; the CLI says the same with the issuer's name. */
|
|
198
|
+
export const CREDIT_EMPTY_TITLE: Record<CreditEmptyCause, string> = {
|
|
199
|
+
instruments: "No debt instrument has been read from this issuer's filings yet.",
|
|
200
|
+
issuer: "This is not an issuer the credit documents cover.",
|
|
201
|
+
};
|
|
202
|
+
/** The maintenance covenant fact a headroom row was computed from, which says its unit. */
|
|
203
|
+
export const covenantFact = (data: Pick<CreditDocumentsPayload, "instruments">, row: Pick<CreditHeadroom, "id" | "instrumentId">): CreditFact | undefined =>
|
|
204
|
+
data.instruments.find((item) => item.id === row.instrumentId)?.facts.find((item) => item.id === row.id);
|
|
205
|
+
/** What a screen row flags, in the words the pane and its exports use. */
|
|
206
|
+
export const screenSignal = (row: Pick<CreditScreenRow, "kind" | "value">) =>
|
|
207
|
+
row.kind === "springing_maturity" ? "Springing maturity" : row.value != null && row.value < 0 ? "Breach" : "Low headroom";
|
|
117
208
|
export const screenRowId = (row: CreditScreenRow) => `${row.symbol}:${row.instrumentId}:${row.kind}:${row.date ?? ""}:${row.reason}`;
|
|
118
209
|
/** Wall currencies, the one with the most instruments first so the wall opens on the issuer's main debt. */
|
|
119
210
|
export function creditCurrencies(data: CreditDocumentsPayload): string[] {
|
|
@@ -208,14 +299,14 @@ type CapitalColumnId = "name" | "facility" | "coupon" | "maturity" | "principal"
|
|
|
208
299
|
const CAPITAL_COLUMNS: Column<CapitalColumnId>[] = [
|
|
209
300
|
{ id: "name", label: "Instrument", width: 24, flexGrow: 1, align: "left" },
|
|
210
301
|
{ id: "facility", label: "Type", width: 12, align: "left" },
|
|
211
|
-
{ id: "coupon", label: "Coupon", width: 20, align: "left" },
|
|
302
|
+
{ id: "coupon", label: "Coupon/Margin", width: 20, align: "left" },
|
|
212
303
|
{ id: "maturity", label: "Maturity", width: 10, align: "left" },
|
|
213
304
|
{ id: "principal", label: "Outstanding", width: 11, align: "right" },
|
|
214
305
|
{ id: "currency", label: "CCY", width: 3, align: "left" },
|
|
215
306
|
{ id: "ranking", label: "Rank", width: 26, align: "left" },
|
|
216
307
|
{ id: "control", label: "CoC put", width: 7, align: "right" },
|
|
217
308
|
];
|
|
218
|
-
/** Text columns take the width their values need, up to the column's own width. */
|
|
309
|
+
/** Text columns take the width their values and their label need, up to the column's own width. */
|
|
219
310
|
const FITTED_CAPITAL = new Set<CapitalColumnId>(["facility", "coupon", "ranking"]);
|
|
220
311
|
const SPARSE_CAPITAL = new Set<CapitalColumnId>(["ranking", "control"]);
|
|
221
312
|
export function capitalColumns(data: CreditDocumentsPayload, width: number): DataTableColumn[] {
|
|
@@ -223,20 +314,19 @@ export function capitalColumns(data: CreditDocumentsPayload, width: number): Dat
|
|
|
223
314
|
const disclosed = (id: CapitalColumnId) => rows.filter((row) => capitalCell(row, id, data) !== NO_VALUE).length;
|
|
224
315
|
// A column nothing discloses is left out; ranking and puts need most instruments.
|
|
225
316
|
const columns = CAPITAL_COLUMNS.filter((column) => column.id === "name" || column.id === "principal" || (SPARSE_CAPITAL.has(column.id) ? disclosed(column.id) * 2 > rows.length : disclosed(column.id) > 0))
|
|
226
|
-
.map((column) => FITTED_CAPITAL.has(column.id) ? { ...column, width: Math.min(column.width, Math.max(4, ...rows.map((row) => displayWidth(capitalCell(row, column.id, data))))) } : column);
|
|
317
|
+
.map((column) => FITTED_CAPITAL.has(column.id) ? { ...column, width: Math.min(column.width, Math.max(4, displayWidth(column.label), ...rows.map((row) => displayWidth(capitalCell(row, column.id, data))))) } : column);
|
|
227
318
|
// One currency is said by the figures; several keep their column at every width.
|
|
228
319
|
const single = new Set(rows.map((row) => row.currency).filter(Boolean)).size <= 1;
|
|
229
320
|
return fitColumns(columns, width, ["control", "ranking", ...(single ? ["currency" as const] : []), "facility", "coupon", "maturity"]);
|
|
230
321
|
}
|
|
322
|
+
/** A revolver reads as drawn of commitment; the strip carries the undrawn total. */
|
|
323
|
+
export const outstandingText = (outstanding: number | null, commitment: number | null): string =>
|
|
324
|
+
commitment != null && commitment !== outstanding ? `${creditAmount(outstanding)} of ${creditAmount(commitment)}` : creditAmount(outstanding);
|
|
231
325
|
export function capitalCell(row: CreditInstrument, column: string, data: CreditDocumentsPayload): string {
|
|
232
326
|
const control = data.changeOfControl.find((entry) => entry.instrumentId === row.id);
|
|
233
327
|
switch (column) {
|
|
234
328
|
case "name": return isClosed(row) ? `${row.name} (${row.lifecycle})` : row.name;
|
|
235
|
-
case "principal":
|
|
236
|
-
// A revolver reads as drawn of commitment; the strip carries the undrawn total.
|
|
237
|
-
const outstanding = creditAmount(instrumentOutstanding(row));
|
|
238
|
-
return row.commitment != null && row.commitment !== instrumentOutstanding(row) ? `${outstanding} of ${creditAmount(row.commitment)}` : outstanding;
|
|
239
|
-
}
|
|
329
|
+
case "principal": return outstandingText(instrumentOutstanding(row), row.commitment);
|
|
240
330
|
case "currency": return row.currency ?? NO_VALUE;
|
|
241
331
|
case "maturity": return row.maturity ?? NO_VALUE;
|
|
242
332
|
case "coupon": return creditCoupon(row);
|
|
@@ -320,7 +410,7 @@ export function maturityAxis(rows: readonly CreditMaturityBucket[]) {
|
|
|
320
410
|
}
|
|
321
411
|
|
|
322
412
|
/** The source fact, not the magnitude, determines a covenant's unit. */
|
|
323
|
-
export function covenantNumber(value: number | null, fact: CreditFact | undefined): string {
|
|
413
|
+
export function covenantNumber(value: number | null, fact: Pick<CreditFact, "currency" | "unit"> | undefined): string {
|
|
324
414
|
if (value === null) return NO_VALUE;
|
|
325
415
|
if (fact?.currency) return `${creditAmount(value)} ${fact.currency}`;
|
|
326
416
|
if (fact?.unit === "ratio" || fact?.unit === "times") return `${value}x`;
|
|
@@ -2,7 +2,7 @@ import { useCallback, useMemo } from "react";
|
|
|
2
2
|
import { isAccessDenied } from "../../../api-client/errors";
|
|
3
3
|
import { usePlanAccess } from "../../../api-client/plan-access";
|
|
4
4
|
import type { CreditDocumentsPayload, CreditHeadroom, CreditInstrument, CreditScreenPayload, CreditScreenRow } from "../../../api-client/credit-documents";
|
|
5
|
-
import { ChartTableHeader, DataTableView, EmptyState, PageStackView, QueryBar, useChartTableSelection, usePaneFooter, usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, type DataTableCell, type DataTableColumn, type PaneHint } from "../../../components";
|
|
5
|
+
import { ChartTableHeader, DataTableView, EmptyState, Notice, PageStackView, QueryBar, useChartTableSelection, usePaneFooter, usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, type DataTableCell, type DataTableColumn, type PaneHint } from "../../../components";
|
|
6
6
|
import { formatCompactAxis } from "../../../components/chart-table";
|
|
7
7
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
8
8
|
import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginAppActions, usePluginPaneState } from "../../../public/react";
|
|
@@ -19,7 +19,7 @@ import { cachedCredit, creditIssuerSymbol, CREDIT_UNAVAILABLE, fetchCreditScreen
|
|
|
19
19
|
import { CreditInstrumentDetail } from "./detail";
|
|
20
20
|
import { useCreditEvidence } from "./evidence";
|
|
21
21
|
import { HeadroomGauge } from "./gauge";
|
|
22
|
-
import { capitalCell, capitalColumns, capitalFigures, comparatorSymbol, covenantColumns, covenantUsage, CREDIT_TABS, creditAmount, covenantNumber, creditCurrencies, creditMaturityRows, creditPercent, creditTab, fieldLabel, headroomTone, instrumentOutstanding, isClosed, maturityAxis, maturityColumns, maturitySeries, maturityShares, NO_VALUE, screenColumns, screenRowId, sentence } from "./model";
|
|
22
|
+
import { capitalCell, capitalColumns, capitalFigures, comparatorSymbol, covenantColumns, covenantFact, covenantReason, covenantUsage, CREDIT_EMPTY_TITLE, creditEmptyCause, CREDIT_TABS, creditAmount, covenantNumber, creditCurrencies, creditMaturityRows, creditPercent, creditTab, fieldLabel, headroomTone, instrumentOutstanding, isClosed, maturityAxis, maturityColumns, maturitySeries, maturityShares, NO_VALUE, screenColumns, screenRowId, screenSignal, sentence } from "./model";
|
|
23
23
|
|
|
24
24
|
type Row = { numbers?: Record<string, number | null>; id: string; values: Record<string, string>; instrumentId?: string; covenant?: CreditHeadroom; screen?: CreditScreenRow; year?: number; locked?: boolean; closed?: boolean };
|
|
25
25
|
const rowId = (row: Row) => row.id;
|
|
@@ -70,10 +70,10 @@ function CreditView({ symbol, width, height, focused, accessKey, needsVerificati
|
|
|
70
70
|
: tab === "maturities" ? maturityColumns(width, preview) : screenColumns(width), [tab, data, width, preview]);
|
|
71
71
|
const figures = useMemo(() => tab === "capital" && data ? capitalFigures(data) : [], [tab, data]);
|
|
72
72
|
const unsortedRows = useMemo<Row[]>(() => {
|
|
73
|
-
if (tab === "screen") return (screenData?.rows ?? []).map((row) => ({ id: screenRowId(row), instrumentId: row.instrumentId, screen: row, numbers: {value:row.value}, values: { symbol: row.symbol, kind: row
|
|
73
|
+
if (tab === "screen") return (screenData?.rows ?? []).map((row) => ({ id: screenRowId(row), instrumentId: row.instrumentId, screen: row, numbers: {value:row.value}, values: { symbol: row.symbol, kind: screenSignal(row), value: creditPercent(row.value), date: row.date ?? NO_VALUE, instrument: row.instrumentName, reason: sentence(row.reason) } }));
|
|
74
74
|
if (!data) return [];
|
|
75
75
|
if (tab === "capital") return data.instruments.map((row) => ({ id: row.id, instrumentId: row.id, closed: isClosed(row), numbers: {principal:instrumentOutstanding(row)}, values: Object.fromEntries(["name", "facility", "coupon", "maturity", "principal", "currency", "ranking", "control"].map((id) => [id, capitalCell(row, id, data)])) }));
|
|
76
|
-
if (tab === "covenants") return data.covenants.map((row) => { const fact=data
|
|
76
|
+
if (tab === "covenants") return data.covenants.map((row) => { const fact=covenantFact(data, row); return ({ id: row.id, instrumentId: row.instrumentId, covenant: row, numbers: {headroom:row.headroomPercent,usage:covenantUsage(row),threshold:row.threshold,current:row.current}, values: { metric: sentence(row.metric), headroom: creditPercent(row.headroomPercent), usage: "", threshold: `${comparatorSymbol(row.comparator, row.inclusive)} ${covenantNumber(row.threshold,fact)}`, current: covenantNumber(row.current,fact), status: fieldLabel(row.status), date: row.asOf, instrument: row.instrumentName } }); });
|
|
77
77
|
const shares = preview ? new Map() : maturityShares(maturityRows);
|
|
78
78
|
return maturityRows.flatMap((row) => row.instruments.map((instrument) => { const id = `${row.year}:${instrument.id}`, share = shares.get(id); return { id, instrumentId: instrument.id, year: row.year, numbers: {year:row.year,principal:instrument.principal,share:share?.share ?? null,cumulative:share?.cumulative ?? null}, values: { year: String(row.year), principal: creditAmount(instrument.principal), share: creditPercent(share?.share), cumulative: creditPercent(share?.cumulative), instruments: instrument.name } }; }));
|
|
79
79
|
}, [data, screenData, tab, maturityRows, preview]);
|
|
@@ -85,11 +85,17 @@ function CreditView({ symbol, width, height, focused, accessKey, needsVerificati
|
|
|
85
85
|
: left === right ? a.id.localeCompare(b.id) : (left < right ? -1 : 1)*(sort.direction === "asc" ? 1 : -1);
|
|
86
86
|
}),[unsortedRows,sort]);
|
|
87
87
|
const selected = rows.find((row) => row.id === selectedId) ?? rows[0] ?? null;
|
|
88
|
+
// The status says Uncomputable; the selected row says why, on the row, as the detail does.
|
|
89
|
+
const selectedCovenant = tab === "covenants" ? selected?.covenant : undefined;
|
|
90
|
+
const whyNoHeadroom = selectedCovenant ? covenantReason(selectedCovenant, data?.instruments.find((row) => row.id === selectedCovenant.instrumentId)?.facts ?? []) : null;
|
|
88
91
|
const screenOpen = rows.find((row) => row.id === openId)?.screen;
|
|
89
92
|
const openInstrument = data?.instruments.find((row) => row.id === openId) ?? (screenOpen ? { id: screenOpen.instrumentId, key: screenOpen.instrumentId, name: screenOpen.instrumentName, currency: screenOpen.currency, facilityType: null, principal: null, commitment: null, drawn: null, maturity: null, facts: screenOpen.evidence } : null);
|
|
90
93
|
const [initialFact] = usePaneSettingValue<string | null>("fact", null);
|
|
91
94
|
const [openFactId, setOpenFact] = usePluginPaneState<string | null>(`credit:fact:open:${openInstrument?.id ?? "none"}`, initialFact);
|
|
92
95
|
const activeSymbol = screenOpen?.symbol ?? symbol;
|
|
96
|
+
// An issuer with nothing in any section says why once, in each tab, instead of a different empty line per tab.
|
|
97
|
+
const emptyCause = data ? creditEmptyCause(data) : null;
|
|
98
|
+
const nothingOnFile = emptyCause ? CREDIT_EMPTY_TITLE[emptyCause] : null;
|
|
93
99
|
const activeCovenant = tab === "covenants" ? data?.covenants.find((row) => row.id === selected?.id && row.instrumentId === openInstrument?.id) ?? null : null;
|
|
94
100
|
const locked = tab === "screen" ? screenData?.lockedRows ?? 0 : data?.lockedRows ?? 0;
|
|
95
101
|
const items = useMemo<Row[]>(() => [...rows, ...Array.from({ length: Math.min(3, locked) }, (_, index) => ({ id: `locked:${index}`, values: {}, locked: true }))], [rows, locked]);
|
|
@@ -180,7 +186,8 @@ function CreditView({ symbol, width, height, focused, accessKey, needsVerificati
|
|
|
180
186
|
getItemKey={rowId} selection={{ kind: "id", selectedId: selected?.id ?? null, getId: rowId, onChange: setSelected }}
|
|
181
187
|
onActivate={(row) => row.locked ? openUpgrade() : setOpen(row.screen ? row.id : row.instrumentId ?? null)}
|
|
182
188
|
renderCell={renderCell}
|
|
183
|
-
emptyStateTitle={tab === "screen" ? "No supported credit signals match these thresholds." : tab === "covenants" ? "No financial maintenance covenants documented." : tab === "maturities" ? "No supported instrument maturity amounts." :
|
|
189
|
+
emptyStateTitle={tab === "screen" ? "No supported credit signals match these thresholds." : nothingOnFile ?? (tab === "covenants" ? "No financial maintenance covenants documented." : tab === "maturities" ? "No supported instrument maturity amounts." : CREDIT_EMPTY_TITLE.instruments)}
|
|
190
|
+
rootAfter={whyNoHeadroom ? <Box paddingX={1} flexShrink={0}><Notice tone="muted">{whyNoHeadroom}</Notice></Box> : undefined}
|
|
184
191
|
emptyStateHint={tab === "screen" && (headroom < 100 || months < 36) ? "Raise either threshold to widen the screen." : undefined}
|
|
185
192
|
selectedTextOverridesCellColor showHorizontalScrollbar freezeFirstColumn resetScrollKey={`${symbol}:${tab}:${selectedCurrency}`}
|
|
186
193
|
bodyAfter={locked > 0 && desktop ? <LockedOverlay rows={Math.min(3, locked)} text={lockedText} onPress={openUpgrade} /> : undefined} />} />
|
|
@@ -30,6 +30,8 @@ export function listViewSources(catalog: ViewSourceCatalog): ViewSourceInfo[] {
|
|
|
30
30
|
const pane = catalog.panes.get(template.paneId);
|
|
31
31
|
if (!pane) continue;
|
|
32
32
|
templated.add(pane.id);
|
|
33
|
+
// An unlisted function (`listed: false`) is not offered, nor its pane in its place.
|
|
34
|
+
if (template.listed === false) continue;
|
|
33
35
|
const headless = template.headless ?? pane.headless;
|
|
34
36
|
if (!headless) continue;
|
|
35
37
|
sources.push({
|