gloomberb 0.16.1 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/package.json +2 -1
- package/src/api-client/beneficial-owners.ts +92 -0
- package/src/api-client/data.ts +26 -8
- package/src/api-client/errors.ts +12 -0
- package/src/api-client/geo.ts +363 -0
- package/src/api-client/index.ts +2 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/paths.ts +30 -1
- package/src/api-client/perps.ts +37 -0
- package/src/api-client/request.ts +5 -2
- package/src/api-client/types.ts +2 -0
- package/src/brokers/account-cache.ts +24 -0
- package/src/cli/commands/command-utils.ts +8 -0
- package/src/cli/commands/crypto-hints.ts +75 -0
- package/src/cli/commands/market.ts +414 -106
- package/src/cli/commands/overview.ts +77 -32
- package/src/cli/commands/ticker.ts +101 -43
- package/src/cli/desktop-pane-shot.ts +34 -4
- package/src/cli/freshness.ts +61 -0
- package/src/cli/help.ts +1 -0
- package/src/cli/helpers.ts +4 -8
- package/src/cli/index.ts +12 -2
- package/src/cli/listing-arg.ts +321 -0
- package/src/cli/pane-functions/access-gate.ts +42 -0
- package/src/cli/pane-functions/catalog.ts +28 -1
- package/src/cli/pane-functions/data.ts +3 -1
- package/src/cli/pane-functions/dom.ts +20 -2
- package/src/cli/pane-functions/freshness.ts +400 -0
- package/src/cli/pane-functions/function-help.ts +27 -20
- package/src/cli/pane-functions/headless.ts +39 -11
- package/src/cli/pane-functions/index.ts +15 -4
- package/src/cli/pane-functions/options.ts +3 -0
- package/src/cli/pane-functions/report.ts +4 -0
- package/src/cli/pane-functions/resolver.ts +35 -7
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +7 -3
- package/src/cli/result.ts +41 -27
- package/src/cli/ticker-resolution.ts +7 -5
- package/src/components/chart/composite/composite-chart.tsx +88 -2536
- package/src/components/chart/composite/drawing-store.ts +59 -0
- package/src/components/chart/composite/legend.tsx +319 -0
- package/src/components/chart/composite/panel-bitmap.ts +139 -0
- package/src/components/chart/composite/panel-gestures.ts +59 -0
- package/src/components/chart/composite/panel-labels.ts +107 -0
- package/src/components/chart/composite/panel-surface.tsx +876 -0
- package/src/components/chart/composite/tool-catalog.ts +98 -0
- package/src/components/chart/composite/toolbar.tsx +186 -0
- package/src/components/chart/composite/tools.ts +8 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
- package/src/components/chart/composite/use-composite-viewport.ts +143 -0
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/list/view.tsx +16 -3
- package/src/components/command-bar/pane-templates/workflow.ts +1 -0
- package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/command-bar/workflow/tickers.ts +6 -0
- package/src/components/data-table/view.tsx +29 -10
- package/src/components/feedback-dialog.tsx +2 -1
- package/src/components/form-modal/field-row.tsx +2 -2
- package/src/components/layout/detached-pane-shell.tsx +6 -3
- package/src/components/layout/pane/content.tsx +23 -10
- package/src/components/layout/pane/error-boundary.tsx +103 -0
- package/src/components/layout/pane/footer/registration.tsx +68 -23
- package/src/components/layout/pane/header.tsx +11 -11
- package/src/components/layout/shell/menu.ts +2 -1
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +14 -3
- package/src/components/metric-treemap/heat-surface.tsx +43 -16
- package/src/components/metric-treemap/heat-tiles.ts +18 -0
- package/src/components/metric-treemap/layout.ts +2 -0
- package/src/components/onboarding/onboarding-keyboard.ts +311 -0
- package/src/components/onboarding/onboarding-wizard.tsx +101 -788
- package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
- package/src/components/onboarding/use-onboarding-progress.ts +298 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
- package/src/components/ticker/list-table-view.tsx +13 -1
- package/src/components/ui/fields.tsx +8 -0
- package/src/components/ui/range-track.tsx +10 -3
- package/src/components/ui/status.tsx +8 -2
- package/src/components/ui/tabs.tsx +7 -4
- package/src/components/ui/text-prompt-dialog.tsx +4 -2
- package/src/data/config/store/normalize.ts +17 -1
- package/src/i18n/es.ts +19 -1
- package/src/i18n/ja.ts +19 -1
- package/src/i18n/ko.ts +19 -1
- package/src/i18n/zh-cn.ts +19 -1
- package/src/i18n/zh-tw.ts +19 -1
- package/src/market-data/coordinator/chart.ts +1 -1
- package/src/market-data/coordinator/quotes.ts +23 -4
- package/src/market-data/fx-closes.ts +72 -0
- package/src/market-data/market/freshness.ts +78 -6
- package/src/market-data/market/session-close-note.ts +57 -0
- package/src/market-data/market/status.ts +151 -0
- package/src/market-data/published-apac-sessions.ts +66 -0
- package/src/market-data/quotes/contributions.ts +2 -0
- package/src/market-data/quotes/resolution.ts +2 -0
- package/src/market-data/quotes/screener-live-quotes.ts +49 -4
- package/src/plugins/builtin/account-management/pane.tsx +4 -0
- package/src/plugins/builtin/alerts/cli.ts +10 -5
- package/src/plugins/builtin/alerts/index.tsx +1 -0
- package/src/plugins/builtin/analytics/metrics.ts +8 -3
- package/src/plugins/builtin/analytics/risk-client.ts +191 -66
- package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
- package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
- package/src/plugins/builtin/analytics/risk-model.ts +45 -17
- package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
- package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
- package/src/plugins/builtin/attention/headless.ts +6 -4
- package/src/plugins/builtin/attention/index.tsx +1 -1
- package/src/plugins/builtin/attention/pane.tsx +1 -1
- package/src/plugins/builtin/awards/headless.ts +1 -0
- package/src/plugins/builtin/awards/pane.tsx +1 -1
- package/src/plugins/builtin/backtest/headless.ts +1 -0
- package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
- package/src/plugins/builtin/buildout/index.tsx +1 -0
- package/src/plugins/builtin/catalysts/headless.ts +3 -1
- package/src/plugins/builtin/cds/headless.ts +2 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
- package/src/plugins/builtin/chart-composer/headless.ts +38 -3
- package/src/plugins/builtin/chart-composer/index.tsx +3 -1
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
- package/src/plugins/builtin/chat/channels.ts +19 -3
- package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
- package/src/plugins/builtin/chat/content/footer.tsx +15 -6
- package/src/plugins/builtin/chat/content/index.tsx +120 -21
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
- package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
- package/src/plugins/builtin/chat/direct-messages.ts +200 -0
- package/src/plugins/builtin/chat/index.tsx +1 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
- package/src/plugins/builtin/chat/message/header.tsx +16 -6
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
- package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
- package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
- package/src/plugins/builtin/chat/message/types.ts +14 -2
- package/src/plugins/builtin/chat/profile-popover.ts +18 -13
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
- package/src/plugins/builtin/cloud/plugin.tsx +1 -0
- package/src/plugins/builtin/cloud/team/module.tsx +1 -0
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/comment-letters/index.tsx +1 -0
- package/src/plugins/builtin/company-attention/headless.ts +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +1 -1
- package/src/plugins/builtin/company-kpis/headless.ts +1 -0
- package/src/plugins/builtin/congress-trades/headless.ts +1 -0
- package/src/plugins/builtin/correlation/compute.ts +59 -16
- package/src/plugins/builtin/correlation/geo.ts +34 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
- package/src/plugins/builtin/correlation/headless.ts +42 -8
- package/src/plugins/builtin/correlation/history.ts +2 -0
- package/src/plugins/builtin/correlation/index.tsx +68 -14
- package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
- package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
- package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
- package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
- package/src/plugins/builtin/correlation/settings.ts +1 -1
- package/src/plugins/builtin/cot/headless.ts +6 -3
- package/src/plugins/builtin/cot/index.tsx +3 -3
- package/src/plugins/builtin/cot/model.ts +36 -3
- package/src/plugins/builtin/cpi/headless.ts +4 -0
- package/src/plugins/builtin/credit-boards/headless.ts +2 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
- package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
- package/src/plugins/builtin/credit-documents/headless.ts +2 -0
- package/src/plugins/builtin/crypto-board/headless.ts +3 -0
- package/src/plugins/builtin/custom-view/loader.ts +1 -1
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
- package/src/plugins/builtin/doe/headless.ts +7 -0
- package/src/plugins/builtin/doe/range-bar.tsx +7 -37
- package/src/plugins/builtin/earnings/headless.ts +5 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
- package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
- package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
- package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
- package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
- package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
- package/src/plugins/builtin/econ/index.tsx +1 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
- package/src/plugins/builtin/equity-screener/headless.ts +2 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
- package/src/plugins/builtin/executives/index.tsx +1 -0
- package/src/plugins/builtin/exposure/headless.ts +3 -1
- package/src/plugins/builtin/fear-greed/headless.ts +2 -0
- package/src/plugins/builtin/filing-events/index.tsx +1 -0
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +39 -10
- package/src/plugins/builtin/futures-curve/index.tsx +3 -3
- package/src/plugins/builtin/futures-curve/model.ts +188 -4
- package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
- package/src/plugins/builtin/gpu/headless.ts +7 -3
- package/src/plugins/builtin/gpu/ladder.tsx +26 -30
- package/src/plugins/builtin/gpu/model.ts +27 -1
- package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
- package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
- package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
- package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
- package/src/plugins/builtin/holders/headless.ts +139 -5
- package/src/plugins/builtin/holders/index.tsx +10 -3
- package/src/plugins/builtin/holders/pane.tsx +20 -3
- package/src/plugins/builtin/holders/report-header.ts +95 -0
- package/src/plugins/builtin/holders/table-model.ts +20 -5
- package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
- package/src/plugins/builtin/holders/types.ts +2 -1
- package/src/plugins/builtin/insider/headless.ts +2 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
- package/src/plugins/builtin/iv-history/headless.ts +4 -1
- package/src/plugins/builtin/jobs/headless.ts +1 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +2 -0
- package/src/plugins/builtin/market-halts/index.tsx +1 -0
- package/src/plugins/builtin/market-heatmap/data.ts +2 -0
- package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
- package/src/plugins/builtin/market-heatmap/model.ts +146 -0
- package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
- package/src/plugins/builtin/market-movers/headless.ts +2 -0
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/members/client.ts +20 -3
- package/src/plugins/builtin/members/headless.ts +5 -3
- package/src/plugins/builtin/members/model.ts +10 -2
- package/src/plugins/builtin/members/pane.tsx +13 -6
- package/src/plugins/builtin/mna/distress/headless.ts +1 -0
- package/src/plugins/builtin/mna/headless.ts +1 -0
- package/src/plugins/builtin/money-markets/headless.ts +1 -0
- package/src/plugins/builtin/news/headless.ts +2 -0
- package/src/plugins/builtin/news/wire/index.tsx +8 -1
- package/src/plugins/builtin/notes/cli.ts +14 -6
- package/src/plugins/builtin/notes/index.tsx +1 -0
- package/src/plugins/builtin/openfda/index.tsx +1 -0
- package/src/plugins/builtin/options-calculator/headless.ts +2 -0
- package/src/plugins/builtin/options-positioning/headless.ts +3 -0
- package/src/plugins/builtin/options-scenario/headless.ts +5 -0
- package/src/plugins/builtin/pe-band/client.ts +48 -3
- package/src/plugins/builtin/pe-band/headless.ts +25 -7
- package/src/plugins/builtin/pe-band/model.ts +92 -16
- package/src/plugins/builtin/pe-band/pane.tsx +33 -16
- package/src/plugins/builtin/perps/client.ts +50 -3
- package/src/plugins/builtin/perps/headless.ts +144 -16
- package/src/plugins/builtin/perps/index.tsx +6 -6
- package/src/plugins/builtin/perps/model.ts +189 -12
- package/src/plugins/builtin/perps/pane.tsx +248 -47
- package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
- package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
- package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
- package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
- package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
- package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
- package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
- package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
- package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
- package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
- package/src/plugins/builtin/power/headless.ts +3 -1
- package/src/plugins/builtin/rate-path/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
- package/src/plugins/builtin/research/analyst-headless.ts +3 -0
- package/src/plugins/builtin/research/events-headless.ts +2 -0
- package/src/plugins/builtin/research/headless.ts +2 -0
- package/src/plugins/builtin/research/index.tsx +1 -0
- package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
- package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
- package/src/plugins/builtin/research-search/index.tsx +1 -0
- package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
- package/src/plugins/builtin/risk-factors/headless.ts +2 -0
- package/src/plugins/builtin/scanner/flow-history.ts +2 -13
- package/src/plugins/builtin/scanner/headless.ts +14 -0
- package/src/plugins/builtin/seasonality/headless.ts +2 -0
- package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
- package/src/plugins/builtin/sec/filing-content.ts +42 -17
- package/src/plugins/builtin/sec/forms.ts +12 -0
- package/src/plugins/builtin/sec/headless.ts +2 -0
- package/src/plugins/builtin/sectors/client.ts +1 -0
- package/src/plugins/builtin/sectors/headless.ts +9 -1
- package/src/plugins/builtin/sectors/sector-model.ts +2 -0
- package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
- package/src/plugins/builtin/shared/report-freshness.ts +57 -0
- package/src/plugins/builtin/shared/research-cells.tsx +4 -9
- package/src/plugins/builtin/short-interest/headless.ts +2 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
- package/src/plugins/builtin/short-volume/headless.ts +1 -0
- package/src/plugins/builtin/social-mentions/headless.ts +1 -0
- package/src/plugins/builtin/supply-chain/headless.ts +7 -1
- package/src/plugins/builtin/supply-chain/index.tsx +10 -1
- package/src/plugins/builtin/supply-chain/model.ts +27 -2
- package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
- package/src/plugins/builtin/themes/headless.ts +3 -0
- package/src/plugins/builtin/thirteenf/headless.ts +110 -25
- package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
- package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
- package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
- package/src/plugins/builtin/time-sales/headless.ts +5 -1
- package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
- package/src/plugins/builtin/volatility/headless.ts +6 -1
- package/src/plugins/builtin/world-indices/headless.ts +2 -0
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
- package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
- package/src/plugins/builtin/world-venue-map/client.ts +45 -0
- package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
- package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
- package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
- package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
- package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
- package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
- package/src/plugins/builtin/world-venue-map/index.ts +67 -4
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
- package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
- package/src/plugins/builtin/world-venue-map/model.ts +7 -4
- package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
- package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
- package/src/plugins/builtin/yield-curve/headless.ts +16 -0
- package/src/plugins/loader.ts +13 -1
- package/src/plugins/registry/pane-settings.ts +1 -0
- package/src/renderers/dom/build-assets.ts +81 -3
- package/src/renderers/dom/data-table/row.tsx +2 -0
- package/src/renderers/dom/desktop/controls.tsx +7 -3
- package/src/renderers/dom/desktop/list-view.tsx +35 -10
- package/src/renderers/dom/desktop/popover.tsx +73 -14
- package/src/renderers/dom/host/input.tsx +14 -3
- package/src/renderers/dom/styles.css +6 -7
- package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
- package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
- package/src/renderers/opentui/host.tsx +5 -0
- package/src/sources/gloomberb-cloud/index.ts +25 -4
- package/src/sources/gloomberb-cloud/normalizers.ts +27 -3
- package/src/sources/provider-router/cached-routes.ts +4 -1
- package/src/sources/provider-router/financials.ts +8 -7
- package/src/sources/provider-router/history.ts +15 -10
- package/src/sources/sec-registrant.ts +75 -0
- package/src/state/app/context/index.tsx +24 -8
- package/src/state/hooks/quote-streaming.ts +41 -26
- package/src/sync/core-contributors.ts +4 -0
- package/src/telemetry/crash-reports.ts +4 -1
- package/src/test-support/cli-context.ts +10 -2
- package/src/theme/heat-colors.ts +266 -84
- package/src/time-series/resolve.ts +1 -1
- package/src/time-series/use-chart-resolution.ts +1 -1
- package/src/types/context-menu.ts +6 -0
- package/src/types/data-provider.ts +2 -0
- package/src/types/financials.ts +3 -0
- package/src/types/headless.ts +79 -2
- package/src/types/plugin.ts +20 -1
- package/src/types/ticker.ts +15 -0
- package/src/ui/host.tsx +14 -0
- package/src/updater/index.ts +9 -11
- package/src/utils/async-deadline.ts +21 -0
- package/src/utils/cli-output.ts +5 -1
- package/src/utils/crypto-pair.ts +41 -0
- package/src/utils/currency-units.ts +14 -0
- package/src/utils/depositary-receipt.ts +26 -0
- package/src/utils/exchanges.ts +72 -0
- package/src/utils/fire-and-forget.ts +24 -0
- package/src/utils/format.ts +13 -0
- package/src/utils/price-earnings.ts +70 -3
- package/src/utils/price-history.ts +58 -13
- package/src/utils/rpc-timeout-error.ts +27 -0
- package/src/utils/sec.ts +13 -1
- package/src/utils/utc-time.ts +79 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Box, Span, Text, TextAttributes, useUiCapabilities } from "../../../ui";
|
|
2
2
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
3
3
|
import { blendHex } from "../../../theme/colors";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { truncateToDisplayWidth } from "../../../utils/format";
|
|
5
|
+
import { gpuAxisLabels, gpuBasisColor, gpuBasisLabel, gpuPrice, type GpuAxisLabel, type GpuLadderRow } from "./model";
|
|
6
6
|
|
|
7
7
|
const LABEL_CELLS = 28;
|
|
8
8
|
const VALUE_CELLS = 17;
|
|
@@ -10,14 +10,13 @@ const VALUE_CELLS = 17;
|
|
|
10
10
|
const position = (value: number, low: number, high: number) => high > low ? (value - low) / (high - low) : 0.5;
|
|
11
11
|
const percent = (ratio: number) => `${(Math.max(0, Math.min(1, ratio)) * 100).toFixed(2)}%`;
|
|
12
12
|
|
|
13
|
-
/** The domain every basis shares, padded
|
|
14
|
-
function gpuLadderDomain(rows: readonly GpuLadderRow[]): { low: number; high: number
|
|
13
|
+
/** The domain every basis shares, padded so the ends never touch a band. */
|
|
14
|
+
function gpuLadderDomain(rows: readonly GpuLadderRow[]): { low: number; high: number } | null {
|
|
15
15
|
if (!rows.length) return null;
|
|
16
16
|
const min = Math.min(...rows.map((row) => row.min));
|
|
17
17
|
const max = Math.max(...rows.map((row) => row.max));
|
|
18
18
|
const pad = Math.max((max - min) * 0.04, max * 0.02, 0.05);
|
|
19
|
-
|
|
20
|
-
return { low, high, ticks: gpuAxisTicks(low, high).filter((tick) => tick >= low && tick <= high) };
|
|
19
|
+
return { low: Math.max(0, min - pad), high: max + pad };
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
/** Terminal cells of one track: thin rule for the range, heavy for the quartiles, a bar at the median. */
|
|
@@ -51,7 +50,10 @@ export function GpuPriceLadder({ rows, width, height, selected }: {
|
|
|
51
50
|
const valueCells = wide ? VALUE_CELLS : 6;
|
|
52
51
|
// Two cells of padding, two gaps, and one spare so the value never clips.
|
|
53
52
|
const trackCellsWidth = Math.max(8, width - labelCells - valueCells - 5);
|
|
54
|
-
const { low, high
|
|
53
|
+
const { low, high } = domain;
|
|
54
|
+
// Labels may hang into the empty label and value columns of the axis row, so the end ticks stay centred.
|
|
55
|
+
const room = { left: labelCells + 1, right: valueCells + 1 };
|
|
56
|
+
const labels = gpuAxisLabels(low, high, trackCellsWidth, room);
|
|
55
57
|
return (
|
|
56
58
|
<Box flexDirection="column" width={width} height={shown.length + 1} paddingX={1} data-gloom-role="gpu-price-ladder">
|
|
57
59
|
{shown.map((row) => {
|
|
@@ -91,37 +93,31 @@ export function GpuPriceLadder({ rows, width, height, selected }: {
|
|
|
91
93
|
</Box>
|
|
92
94
|
);
|
|
93
95
|
})}
|
|
94
|
-
<
|
|
95
|
-
<Box width={labelCells} flexShrink={0} />
|
|
96
|
-
<GpuLadderAxis ticks={ticks} low={low} high={high} width={trackCellsWidth} desktop={desktop} />
|
|
97
|
-
</Box>
|
|
96
|
+
<GpuLadderAxis labels={labels} width={trackCellsWidth} room={room} desktop={desktop} />
|
|
98
97
|
</Box>
|
|
99
98
|
);
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Each label centred on its tick, on the scale the bands and markers use. The
|
|
103
|
+
* desktop places real elements by percentage, as it does the markers; the
|
|
104
|
+
* terminal writes the whole axis row so an end label can hang past the track.
|
|
105
|
+
*/
|
|
106
|
+
function GpuLadderAxis({ labels, width, room, desktop }: { labels: readonly GpuAxisLabel[]; width: number; room: { left: number; right: number }; desktop: boolean }) {
|
|
103
107
|
const colors = useThemeColors();
|
|
104
|
-
const label = (tick: number) => `$${Number.isInteger(tick) ? tick : tick.toFixed(1)}`;
|
|
105
108
|
if (desktop) {
|
|
106
109
|
return (
|
|
107
|
-
<Box
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
})}
|
|
110
|
+
<Box flexDirection="row" height={1}>
|
|
111
|
+
<Box width={room.left} flexShrink={0} />
|
|
112
|
+
<Box width={width} height={1} flexShrink={0} style={{ position: "relative" }}>
|
|
113
|
+
{labels.map((label) => <Box key={label.value} style={{ position: "absolute", left: percent(label.ratio), transform: "translateX(-50%)", whiteSpace: "nowrap" }}>
|
|
114
|
+
<Text fg={colors.textDim}>{label.text}</Text>
|
|
115
|
+
</Box>)}
|
|
116
|
+
</Box>
|
|
115
117
|
</Box>
|
|
116
118
|
);
|
|
117
119
|
}
|
|
118
|
-
const line = Array.from({ length: width }, () => " ");
|
|
119
|
-
for (const [
|
|
120
|
-
|
|
121
|
-
const center = Math.round(position(tick, low, high) * (width - 1));
|
|
122
|
-
const start = index === 0 ? 0 : index === ticks.length - 1 ? width - displayWidth(text) : center - Math.floor(text.length / 2);
|
|
123
|
-
if (start < 0 || start + text.length > width || line.slice(Math.max(0, start - 1), start + text.length + 1).some((char) => char !== " ")) continue;
|
|
124
|
-
for (let i = 0; i < text.length; i++) line[start + i] = text[i]!;
|
|
125
|
-
}
|
|
126
|
-
return <Box width={width} height={1} flexShrink={0}><Text fg={colors.textDim}>{line.join("")}</Text></Box>;
|
|
120
|
+
const line = Array.from({ length: room.left + width + room.right }, () => " ");
|
|
121
|
+
for (const label of labels) for (let i = 0; i < label.text.length; i++) line[room.left + label.start + i] = label.text[i]!;
|
|
122
|
+
return <Box height={1} flexShrink={0}><Text fg={colors.textDim}>{line.join("").trimEnd()}</Text></Box>;
|
|
127
123
|
}
|
|
@@ -151,7 +151,7 @@ export function gpuPriceLadder(rows: readonly GpuBoardRow[], model: string): Gpu
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
/** Round dollar ticks for a price axis: one, two or five times a power of ten, four to six of them. */
|
|
154
|
-
|
|
154
|
+
function gpuAxisTicks(low: number, high: number): number[] {
|
|
155
155
|
if (!(high > low)) return [low];
|
|
156
156
|
const raw = (high - low) / 5;
|
|
157
157
|
const power = 10 ** Math.floor(Math.log10(raw));
|
|
@@ -161,6 +161,32 @@ export function gpuAxisTicks(low: number, high: number): number[] {
|
|
|
161
161
|
return ticks;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
export interface GpuAxisLabel { value: number; text: string; ratio: number; start: number }
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Labels for a price axis `cells` wide over `low`..`high`: each centred on its
|
|
168
|
+
* tick's own value, on the linear scale the bands and markers use (`ratio`, and
|
|
169
|
+
* `start`, the first cell on the terminal), with the decimals the tick step
|
|
170
|
+
* needs so neighbours never read alike ($2.0 $2.1 $2.2, $2.05 $2.10). A label
|
|
171
|
+
* that would touch its neighbour or run past the room beside the axis is left
|
|
172
|
+
* out, never moved off its value.
|
|
173
|
+
*/
|
|
174
|
+
export function gpuAxisLabels(low: number, high: number, cells: number, room: { left: number; right: number } = { left: 0, right: 0 }): GpuAxisLabel[] {
|
|
175
|
+
const ticks = gpuAxisTicks(low, high).filter((tick) => tick >= low && tick <= high);
|
|
176
|
+
const digits = [0, 1, 2, 3].find((places) => ticks.every((tick) => Math.abs(Number(tick.toFixed(places)) - tick) < 1e-9)) ?? 3;
|
|
177
|
+
const labels: GpuAxisLabel[] = [];
|
|
178
|
+
for (const value of ticks) {
|
|
179
|
+
const text = `$${value.toFixed(digits)}`;
|
|
180
|
+
const ratio = high > low ? (value - low) / (high - low) : 0.5;
|
|
181
|
+
// The cell a track draws this value in, so the label's middle sits on the marker's cell.
|
|
182
|
+
const start = Math.round(ratio * (cells - 1)) - Math.floor(text.length / 2);
|
|
183
|
+
const previous = labels.at(-1);
|
|
184
|
+
if (start < -room.left || start + text.length > cells + room.right || (previous && start <= previous.start + previous.text.length)) continue;
|
|
185
|
+
labels.push({ value, text, ratio, start });
|
|
186
|
+
}
|
|
187
|
+
return labels;
|
|
188
|
+
}
|
|
189
|
+
|
|
164
190
|
/** Published notices use their effective date; observed changes use the dated evidence. */
|
|
165
191
|
export const gpuEventDate = (event: Pick<GpuEvent, "origin" | "effectiveAt" | "observedAt">) =>
|
|
166
192
|
event.origin === "published" ? event.effectiveAt ?? event.observedAt : event.observedAt;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { apiClient } from "../../../api-client";
|
|
2
|
+
import type {
|
|
3
|
+
BeneficialOwnerFiling,
|
|
4
|
+
BeneficialOwnerReportingPerson,
|
|
5
|
+
BeneficialOwnerStatus,
|
|
6
|
+
BeneficialOwnersForm,
|
|
7
|
+
BeneficialOwnersPayload,
|
|
8
|
+
} from "../../../api-client/beneficial-owners";
|
|
9
|
+
import { ApiRequestError, isAccessDenied } from "../../../api-client/errors";
|
|
10
|
+
import { createPluginCache } from "../../../data/plugin-cache";
|
|
11
|
+
import { finiteOrNull, recordOrNull } from "../../../utils/guards";
|
|
12
|
+
import { cachedCloudResource, loadCloudResource, type CloudResource } from "../shared/cloud-resource";
|
|
13
|
+
import { CLOUD_SESSION_REQUIRED } from "../shared/research-cloud-session";
|
|
14
|
+
|
|
15
|
+
/** One page of the route; the loader follows `nextOffset` until it has every report. */
|
|
16
|
+
const PAGE_SIZE = 100;
|
|
17
|
+
/** More than any issuer has filers; a history this long is cut and marked incomplete. */
|
|
18
|
+
const MAX_ROWS = 2_000;
|
|
19
|
+
|
|
20
|
+
export const beneficialOwnersCache = createPluginCache<BeneficialOwnersPayload>({
|
|
21
|
+
kind: "beneficial-owners",
|
|
22
|
+
source: "gloom-cloud",
|
|
23
|
+
schemaVersion: 1,
|
|
24
|
+
policy: { staleMs: 30 * 60_000, expireMs: 7 * 24 * 60 * 60_000 },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export interface BeneficialOwnersRequest {
|
|
28
|
+
form?: BeneficialOwnersForm;
|
|
29
|
+
/** Every report instead of the latest per filer. */
|
|
30
|
+
history?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type BeneficialOwnersClient = Pick<typeof apiClient, "getCloudSecBeneficialOwners">;
|
|
34
|
+
|
|
35
|
+
const text = (value: unknown): string | null => typeof value === "string" && value.trim() ? value.trim() : null;
|
|
36
|
+
const isoDate = (value: unknown): string | null => {
|
|
37
|
+
const raw = text(value);
|
|
38
|
+
return raw && /^\d{4}-\d{2}-\d{2}/.test(raw) ? raw.slice(0, 10) : null;
|
|
39
|
+
};
|
|
40
|
+
const cikOrNull = (value: unknown): string | null => {
|
|
41
|
+
const raw = typeof value === "number" ? String(value) : text(value);
|
|
42
|
+
return raw && /\d/.test(raw) ? raw : null;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function normalizeReportingPerson(value: unknown): BeneficialOwnerReportingPerson | null {
|
|
46
|
+
const raw = recordOrNull(value);
|
|
47
|
+
const name = text(raw?.name);
|
|
48
|
+
if (!raw || !name) return null;
|
|
49
|
+
return { name, cik: cikOrNull(raw.cik), percentOfClass: finiteOrNull(raw.percentOfClass), shares: finiteOrNull(raw.shares) };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const STATUSES: readonly BeneficialOwnerStatus[] = ["holder", "below-threshold", "exited"];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A report as the table can trust it. Fields the route adds later pass
|
|
56
|
+
* through; a report without a filer, accession or filing date is dropped,
|
|
57
|
+
* and a number that is not one stays null instead of being guessed. History
|
|
58
|
+
* reports carry no previous report or status, and none is made up for them.
|
|
59
|
+
*/
|
|
60
|
+
function normalizeBeneficialOwnerFiling(value: unknown): BeneficialOwnerFiling | null {
|
|
61
|
+
const raw = recordOrNull(value);
|
|
62
|
+
if (!raw) return null;
|
|
63
|
+
const filerName = text(raw.filerName);
|
|
64
|
+
const accessionNumber = text(raw.accessionNumber);
|
|
65
|
+
const filingDate = isoDate(raw.filingDate);
|
|
66
|
+
const form = text(raw.form) ?? "";
|
|
67
|
+
const kind = raw.kind === "13D" || raw.kind === "13G" ? raw.kind
|
|
68
|
+
: /13D/i.test(form) ? "13D" : /13G/i.test(form) ? "13G" : null;
|
|
69
|
+
if (!filerName || !accessionNumber || !filingDate || !kind) return null;
|
|
70
|
+
const source = raw.source === "xml" || raw.source === "text" ? raw.source : "index";
|
|
71
|
+
return {
|
|
72
|
+
...raw,
|
|
73
|
+
filerCik: cikOrNull(raw.filerCik),
|
|
74
|
+
filerName,
|
|
75
|
+
form: form || `SCHEDULE ${kind}`,
|
|
76
|
+
kind,
|
|
77
|
+
amendment: raw.amendment === true || /\/A\b/i.test(form),
|
|
78
|
+
amendmentNo: finiteOrNull(raw.amendmentNo),
|
|
79
|
+
percentOfClass: finiteOrNull(raw.percentOfClass),
|
|
80
|
+
shares: finiteOrNull(raw.shares),
|
|
81
|
+
classTitle: text(raw.classTitle),
|
|
82
|
+
cusip: text(raw.cusip),
|
|
83
|
+
eventDate: isoDate(raw.eventDate),
|
|
84
|
+
filingDate,
|
|
85
|
+
accessionNumber,
|
|
86
|
+
filingUrl: text(raw.filingUrl) ?? "",
|
|
87
|
+
previousPercent: "previousPercent" in raw ? finiteOrNull(raw.previousPercent) : undefined,
|
|
88
|
+
previousFilingDate: "previousFilingDate" in raw ? isoDate(raw.previousFilingDate) : undefined,
|
|
89
|
+
status: STATUSES.includes(raw.status as BeneficialOwnerStatus) ? raw.status as BeneficialOwnerStatus : undefined,
|
|
90
|
+
provisional: raw.provisional === true ? true : undefined,
|
|
91
|
+
source,
|
|
92
|
+
reportingPersons: Array.isArray(raw.reportingPersons)
|
|
93
|
+
? raw.reportingPersons.map(normalizeReportingPerson).filter((person) => person != null)
|
|
94
|
+
: [],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function normalizeFilings(value: unknown): BeneficialOwnerFiling[] {
|
|
99
|
+
return Array.isArray(value)
|
|
100
|
+
? value.map(normalizeBeneficialOwnerFiling).filter((filing) => filing != null)
|
|
101
|
+
: [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeBeneficialOwnersPayload(value: unknown, ticker: string): BeneficialOwnersPayload {
|
|
105
|
+
const raw = recordOrNull(value);
|
|
106
|
+
if (!raw || !Array.isArray(raw.owners)) throw new Error("The server returned an invalid 13D/13G list");
|
|
107
|
+
const coverage = recordOrNull(raw.coverage);
|
|
108
|
+
const nextOffset = finiteOrNull(raw.nextOffset);
|
|
109
|
+
return {
|
|
110
|
+
...raw,
|
|
111
|
+
ticker: text(raw.ticker) ?? ticker,
|
|
112
|
+
cik: cikOrNull(raw.cik) ?? "",
|
|
113
|
+
companyName: text(raw.companyName) ?? "",
|
|
114
|
+
asOf: text(raw.asOf),
|
|
115
|
+
owners: normalizeFilings(raw.owners),
|
|
116
|
+
...(Array.isArray(raw.filings) ? { filings: normalizeFilings(raw.filings) } : {}),
|
|
117
|
+
hasMore: raw.hasMore === true && nextOffset != null,
|
|
118
|
+
nextOffset,
|
|
119
|
+
coverage: coverage ? {
|
|
120
|
+
...coverage,
|
|
121
|
+
from: isoDate(coverage.from),
|
|
122
|
+
filings: finiteOrNull(coverage.filings) ?? 0,
|
|
123
|
+
parsed: finiteOrNull(coverage.parsed) ?? 0,
|
|
124
|
+
unparsed: finiteOrNull(coverage.unparsed) ?? 0,
|
|
125
|
+
unavailable: finiteOrNull(coverage.unavailable) ?? 0,
|
|
126
|
+
} : null,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function describeFailure(error: unknown, ticker: string): unknown {
|
|
131
|
+
if (isAccessDenied(error)) return new Error(CLOUD_SESSION_REQUIRED);
|
|
132
|
+
// An unknown ticker, and a server that does not serve the route yet.
|
|
133
|
+
if (error instanceof ApiRequestError && error.status === 404) {
|
|
134
|
+
return new Error(`13D/13G filings are not available for ${ticker}.`);
|
|
135
|
+
}
|
|
136
|
+
return error;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Every page of the issuer's reports: the latest per filer, or with
|
|
141
|
+
* `history` every report. A list cut at the row cap keeps `hasMore`.
|
|
142
|
+
*/
|
|
143
|
+
export async function fetchBeneficialOwners(
|
|
144
|
+
ticker: string,
|
|
145
|
+
{ form = "all", history = false }: BeneficialOwnersRequest = {},
|
|
146
|
+
{ client = apiClient, signal }: { client?: BeneficialOwnersClient; signal?: AbortSignal } = {},
|
|
147
|
+
): Promise<BeneficialOwnersPayload> {
|
|
148
|
+
const symbol = ticker.trim().toUpperCase();
|
|
149
|
+
try {
|
|
150
|
+
let offset = 0;
|
|
151
|
+
let first: BeneficialOwnersPayload | null = null;
|
|
152
|
+
const owners: BeneficialOwnerFiling[] = [];
|
|
153
|
+
const filings: BeneficialOwnerFiling[] = [];
|
|
154
|
+
for (;;) {
|
|
155
|
+
const page = normalizeBeneficialOwnersPayload(await client.getCloudSecBeneficialOwners({
|
|
156
|
+
ticker: symbol,
|
|
157
|
+
form,
|
|
158
|
+
history,
|
|
159
|
+
limit: PAGE_SIZE,
|
|
160
|
+
offset,
|
|
161
|
+
}, { signal }), symbol);
|
|
162
|
+
first ??= page;
|
|
163
|
+
owners.push(...page.owners);
|
|
164
|
+
filings.push(...(page.filings ?? []));
|
|
165
|
+
const loaded = history ? filings.length : owners.length;
|
|
166
|
+
const nextOffset = page.nextOffset ?? 0;
|
|
167
|
+
if (!page.hasMore || nextOffset <= offset || loaded >= MAX_ROWS) {
|
|
168
|
+
return {
|
|
169
|
+
...first,
|
|
170
|
+
asOf: page.asOf ?? first.asOf,
|
|
171
|
+
coverage: page.coverage ?? first.coverage,
|
|
172
|
+
owners: dedupe(owners),
|
|
173
|
+
...(history ? { filings: dedupe(filings) } : {}),
|
|
174
|
+
hasMore: page.hasMore,
|
|
175
|
+
nextOffset: page.nextOffset,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
offset = nextOffset;
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw describeFailure(error, symbol);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Pages can overlap when a report lands between two requests. */
|
|
186
|
+
function dedupe(rows: BeneficialOwnerFiling[]): BeneficialOwnerFiling[] {
|
|
187
|
+
const seen = new Set<string>();
|
|
188
|
+
return rows.filter((row) => {
|
|
189
|
+
const key = `${row.accessionNumber}:${row.filerCik ?? row.filerName}`;
|
|
190
|
+
if (seen.has(key)) return false;
|
|
191
|
+
seen.add(key);
|
|
192
|
+
return true;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const cacheKey = (ticker: string) => ticker.trim().toUpperCase();
|
|
197
|
+
|
|
198
|
+
export function cachedBeneficialOwners(ticker: string): CloudResource<BeneficialOwnersPayload> | null {
|
|
199
|
+
return cachedCloudResource(beneficialOwnersCache, cacheKey(ticker));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** The pane's list: the latest report per filer, both kinds, cached on disk. */
|
|
203
|
+
export async function loadBeneficialOwners(ticker: string, force = false): Promise<CloudResource<BeneficialOwnersPayload>> {
|
|
204
|
+
try {
|
|
205
|
+
return await loadCloudResource(beneficialOwnersCache, cacheKey(ticker), () => fetchBeneficialOwners(ticker), { force });
|
|
206
|
+
} catch (error) {
|
|
207
|
+
throw describeFailure(error, cacheKey(ticker));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import type { BeneficialOwnerFiling, BeneficialOwnerStatus } from "../../../api-client/beneficial-owners";
|
|
2
|
+
import type { DataTableColumn } from "../../../components";
|
|
3
|
+
import type { HolderRecord } from "../../../types/financials";
|
|
4
|
+
import { clipToDisplayWidth, displayWidth, formatCompact } from "../../../utils/format";
|
|
5
|
+
import { compareSortValues, type SortDirection } from "../../../utils/sort-values";
|
|
6
|
+
import { matchFilerToHolder, type Holder13FMatch } from "./thirteenf-match";
|
|
7
|
+
|
|
8
|
+
export type BeneficialColumnId =
|
|
9
|
+
| "filer"
|
|
10
|
+
| "form"
|
|
11
|
+
| "percentOfClass"
|
|
12
|
+
| "change"
|
|
13
|
+
| "shares"
|
|
14
|
+
| "eventDate"
|
|
15
|
+
| "filingDate"
|
|
16
|
+
| "thirteenF";
|
|
17
|
+
export type BeneficialColumn = DataTableColumn & { id: BeneficialColumnId };
|
|
18
|
+
|
|
19
|
+
export interface BeneficialSortPreference {
|
|
20
|
+
columnId: BeneficialColumnId;
|
|
21
|
+
direction: SortDirection;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_BENEFICIAL_SORT: BeneficialSortPreference = { columnId: "percentOfClass", direction: "desc" };
|
|
25
|
+
|
|
26
|
+
/** What the filer did in its latest 13F against the quarter before. */
|
|
27
|
+
export interface ThirteenFAction {
|
|
28
|
+
label: string;
|
|
29
|
+
/** The share change as a fraction of the prior position: NEW sorts first, EXIT is -1. */
|
|
30
|
+
value: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface BeneficialOwnerRow {
|
|
34
|
+
id: string;
|
|
35
|
+
filing: BeneficialOwnerFiling;
|
|
36
|
+
/** `13G/A`: the kind, and `/A` for an amendment. */
|
|
37
|
+
formLabel: string;
|
|
38
|
+
/** Percent points against the filer's report before this one. */
|
|
39
|
+
changePoints: number | null;
|
|
40
|
+
thirteenF: ThirteenFAction | null;
|
|
41
|
+
/** The stake the report discloses; null when the report gives neither a percentage nor shares. */
|
|
42
|
+
stake: BeneficialOwnerStatus | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Holder rows the 13F join reads: the Table tab's rows, keyed by their id. */
|
|
46
|
+
type JoinableHolder = HolderRecord & { id: string };
|
|
47
|
+
|
|
48
|
+
/** The Schedule 13D/13G reporting threshold, in percent of the class. */
|
|
49
|
+
const REPORTING_THRESHOLD = 5;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The stake a report discloses, from its own figures first, as the route
|
|
53
|
+
* reads them. A stake under 5% is still a stake; only a report whose figures
|
|
54
|
+
* are all zero (0% and zero shares, or one of them with the other missing)
|
|
55
|
+
* is an exit, and a contradictory one is not. A report with neither figure
|
|
56
|
+
* keeps the route's word for a stake under the threshold, but is never read
|
|
57
|
+
* as an exit.
|
|
58
|
+
*/
|
|
59
|
+
export function disclosedStake(
|
|
60
|
+
filing: Pick<BeneficialOwnerFiling, "percentOfClass" | "shares" | "status">,
|
|
61
|
+
): BeneficialOwnerStatus | null {
|
|
62
|
+
const { percentOfClass: percent, shares } = filing;
|
|
63
|
+
const reported = [percent, shares].filter((value) => value != null);
|
|
64
|
+
if (reported.length > 0 && reported.every((value) => value === 0)) return "exited";
|
|
65
|
+
if (percent != null) return percent < REPORTING_THRESHOLD ? "below-threshold" : "holder";
|
|
66
|
+
return filing.status === "holder" || filing.status === "below-threshold" ? filing.status : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function signedPercent(fraction: number): string {
|
|
70
|
+
const rounded = Math.round(fraction * 100);
|
|
71
|
+
return rounded > 0 ? `+${rounded}%` : rounded < 0 ? `${rounded}%` : "0%";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The quarter's action from a 13F holder row. A position that grew from
|
|
76
|
+
* nothing is NEW, one reported at zero shares after a sale EXIT; without a
|
|
77
|
+
* reported change it is unknown, never "no change".
|
|
78
|
+
*/
|
|
79
|
+
export function thirteenFAction(holder: HolderRecord | undefined): ThirteenFAction | null {
|
|
80
|
+
if (!holder) return null;
|
|
81
|
+
const { shares, changeShares, changePercent } = holder;
|
|
82
|
+
if (shares === 0 && changeShares != null && changeShares < 0) return { label: "EXIT", value: -1 };
|
|
83
|
+
if (shares != null && changeShares != null) {
|
|
84
|
+
const prior = shares - changeShares;
|
|
85
|
+
if (changeShares > 0 && prior === 0) return { label: "NEW", value: Number.POSITIVE_INFINITY };
|
|
86
|
+
if (prior > 0) return { label: signedPercent(changeShares / prior), value: changeShares / prior };
|
|
87
|
+
}
|
|
88
|
+
if (changePercent != null && Number.isFinite(changePercent)) {
|
|
89
|
+
// The Table tab reads a provider's change the same way: a fraction up to 1, percent points above.
|
|
90
|
+
const fraction = Math.abs(changePercent) <= 1 ? changePercent : changePercent / 100;
|
|
91
|
+
return { label: signedPercent(fraction), value: fraction };
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function beneficialFormLabel(filing: Pick<BeneficialOwnerFiling, "kind" | "amendment">): string {
|
|
97
|
+
return `${filing.kind}${filing.amendment ? "/A" : ""}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function pointChange(filing: BeneficialOwnerFiling): number | null {
|
|
101
|
+
if (filing.percentOfClass == null || filing.previousPercent == null) return null;
|
|
102
|
+
return Math.round((filing.percentOfClass - filing.previousPercent) * 100) / 100;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* One row per report, joined to the filer's 13F holder row (CIK first, then
|
|
107
|
+
* name) for the quarter's action.
|
|
108
|
+
*/
|
|
109
|
+
export function buildBeneficialRows(
|
|
110
|
+
filings: readonly BeneficialOwnerFiling[],
|
|
111
|
+
holders: readonly JoinableHolder[] = [],
|
|
112
|
+
fundMatches: ReadonlyMap<string, Pick<Holder13FMatch, "cik">> = new Map(),
|
|
113
|
+
): BeneficialOwnerRow[] {
|
|
114
|
+
return filings.map((filing) => ({
|
|
115
|
+
id: `${filing.accessionNumber}:${filing.filerCik ?? filing.filerName}`,
|
|
116
|
+
filing,
|
|
117
|
+
formLabel: beneficialFormLabel(filing),
|
|
118
|
+
changePoints: pointChange(filing),
|
|
119
|
+
thirteenF: thirteenFAction(matchFilerToHolder(filing, holders, fundMatches)),
|
|
120
|
+
stake: disclosedStake(filing),
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function sortValue(row: BeneficialOwnerRow, columnId: BeneficialColumnId): string | number | null {
|
|
125
|
+
switch (columnId) {
|
|
126
|
+
case "filer": return row.filing.filerName;
|
|
127
|
+
case "form": return row.formLabel;
|
|
128
|
+
case "percentOfClass": return row.filing.percentOfClass;
|
|
129
|
+
case "change": return row.changePoints;
|
|
130
|
+
case "shares": return row.filing.shares;
|
|
131
|
+
case "eventDate": return row.filing.eventDate;
|
|
132
|
+
case "filingDate": return row.filing.filingDate;
|
|
133
|
+
case "thirteenF": {
|
|
134
|
+
const value = row.thirteenF?.value;
|
|
135
|
+
// NEW leads a descending sort without leaving the finite range compareSortValues keeps.
|
|
136
|
+
return value == null ? null : value === Number.POSITIVE_INFINITY ? Number.MAX_SAFE_INTEGER : value;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Filers over the threshold (or not saying) first, then stakes under 5%, then exits. */
|
|
142
|
+
function stakeGroup(row: BeneficialOwnerRow): number {
|
|
143
|
+
return row.stake === "exited" ? 2 : row.stake === "below-threshold" ? 1 : 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Sorted by the column within each stake group, whatever the direction; ties newest filing first. */
|
|
147
|
+
export function sortBeneficialRows(rows: readonly BeneficialOwnerRow[], preference: BeneficialSortPreference): BeneficialOwnerRow[] {
|
|
148
|
+
return [...rows].sort((left, right) => stakeGroup(left) - stakeGroup(right)
|
|
149
|
+
|| compareSortValues(sortValue(left, preference.columnId), sortValue(right, preference.columnId), preference.direction)
|
|
150
|
+
|| right.filing.filingDate.localeCompare(left.filing.filingDate)
|
|
151
|
+
|| left.filing.filerName.localeCompare(right.filing.filerName));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Widths as the table draws them: never narrower than the header and its sort marker.
|
|
155
|
+
const FORM_WIDTH = 6;
|
|
156
|
+
/** `22.2%`, `4.9% <5%`, `0.0% EXIT`. */
|
|
157
|
+
const PERCENT_WIDTH = 9;
|
|
158
|
+
const CHANGE_WIDTH = 8;
|
|
159
|
+
const SHARES_WIDTH = 8;
|
|
160
|
+
const DATE_WIDTH = 10;
|
|
161
|
+
const THIRTEEN_F_WIDTH = 5;
|
|
162
|
+
const MIN_FILER_WIDTH = 14;
|
|
163
|
+
/** The longest filer names and a group count; a wider pane keeps the figures beside the names. */
|
|
164
|
+
const MAX_FILER_WIDTH = 56;
|
|
165
|
+
/** Below this the event date gives its room to the filer's name; the filing date stays. */
|
|
166
|
+
const EVENT_FILER_WIDTH = 20;
|
|
167
|
+
|
|
168
|
+
export function buildBeneficialColumns(width: number): BeneficialColumn[] {
|
|
169
|
+
const fixed = FORM_WIDTH + PERCENT_WIDTH + CHANGE_WIDTH + SHARES_WIDTH + DATE_WIDTH + THIRTEEN_F_WIDTH;
|
|
170
|
+
// Two cells of padding and one gap after every column.
|
|
171
|
+
const roomWithEvent = width - 2 - 8 - fixed - DATE_WIDTH;
|
|
172
|
+
const showEvent = roomWithEvent >= EVENT_FILER_WIDTH;
|
|
173
|
+
const filerWidth = Math.min(MAX_FILER_WIDTH, Math.max(MIN_FILER_WIDTH, showEvent ? roomWithEvent : roomWithEvent + DATE_WIDTH + 1));
|
|
174
|
+
return [
|
|
175
|
+
{ id: "filer", label: "FILER", width: filerWidth, align: "left" },
|
|
176
|
+
{ id: "form", label: "FORM", width: FORM_WIDTH, align: "left" },
|
|
177
|
+
{ id: "percentOfClass", label: "% CLASS", width: PERCENT_WIDTH, align: "right" },
|
|
178
|
+
{ id: "change", label: "CHG", width: CHANGE_WIDTH, align: "right" },
|
|
179
|
+
{ id: "shares", label: "SHARES", width: SHARES_WIDTH, align: "right" },
|
|
180
|
+
...(showEvent ? [{ id: "eventDate" as const, label: "EVENT", width: DATE_WIDTH, align: "right" as const }] : []),
|
|
181
|
+
{ id: "filingDate", label: "FILED", width: DATE_WIDTH, align: "right" },
|
|
182
|
+
{ id: "thirteenF", label: "13F", width: THIRTEEN_F_WIDTH, align: "right" },
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Entity suffixes a narrow column drops before it cuts into the name itself. */
|
|
187
|
+
const LEGAL_SUFFIX = /(?:[,\s]+(?:L\.?L\.?C|L\.?L\.?P|L\.?P|INC|LTD|LIMITED|CORP|CORPORATION|CO|PLC|N\.?A|S\.?A|AG|GMBH)\.?)+$/i;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The filer's name in `width` cells, with ` +N` for the other reporting
|
|
191
|
+
* persons of a group. The entity suffix goes first, then the name is cut
|
|
192
|
+
* behind an ellipsis; the count is never cut.
|
|
193
|
+
*/
|
|
194
|
+
export function formatFilerName(name: string, otherPersons: number, width: number): string {
|
|
195
|
+
const suffix = otherPersons > 0 ? ` +${otherPersons}` : "";
|
|
196
|
+
const room = Math.max(1, width - displayWidth(suffix));
|
|
197
|
+
if (displayWidth(name) <= room) return `${name}${suffix}`;
|
|
198
|
+
const short = name.replace(LEGAL_SUFFIX, "").trim() || name;
|
|
199
|
+
return `${clipToDisplayWidth(short, room)}${suffix}`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** The marker after a stake that is not a 5% holding: under the threshold, or reported at zero. */
|
|
203
|
+
export function stakeMarker(stake: BeneficialOwnerStatus | null): string {
|
|
204
|
+
return stake === "below-threshold" ? "<5%" : stake === "exited" ? "EXIT" : "";
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A percent of class to one decimal, or two when one would round a stake
|
|
209
|
+
* under 5% up to `5.0%`: 4.96 reads `4.96%`.
|
|
210
|
+
*/
|
|
211
|
+
export function formatClassPercent(value: number): string {
|
|
212
|
+
const short = value.toFixed(1);
|
|
213
|
+
if (value >= REPORTING_THRESHOLD || Number(short) < REPORTING_THRESHOLD) return `${short}%`;
|
|
214
|
+
return `${(Math.floor(value * 100) / 100).toFixed(2)}%`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The reported percent of class, as filed: `4.9%` stays `4.9%`. With
|
|
219
|
+
* `marker`, a stake under 5% reads `4.9% <5%` and a report of zero
|
|
220
|
+
* `0.0% EXIT`. A missing figure is a dash, or the bare marker when the
|
|
221
|
+
* route says which side of 5% the stake is on.
|
|
222
|
+
*/
|
|
223
|
+
export function formatPercentOfClass(
|
|
224
|
+
row: Pick<BeneficialOwnerRow, "stake" | "filing">,
|
|
225
|
+
{ marker = false }: { marker?: boolean } = {},
|
|
226
|
+
): string {
|
|
227
|
+
const value = row.filing.percentOfClass;
|
|
228
|
+
const mark = marker ? stakeMarker(row.stake) : "";
|
|
229
|
+
if (value == null) return mark || "-";
|
|
230
|
+
return mark ? `${formatClassPercent(value)} ${mark}` : formatClassPercent(value);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function formatPointChange(value: number | null): string {
|
|
234
|
+
if (value == null) return "";
|
|
235
|
+
const rounded = Math.round(value * 10) / 10;
|
|
236
|
+
if (rounded === 0) return "0.0 pt";
|
|
237
|
+
return `${rounded > 0 ? "+" : ""}${rounded.toFixed(1)} pt`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function formatOwnerShares(value: number | null): string {
|
|
241
|
+
return value == null ? "-" : formatCompact(value);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A row as `holders --form` and `fn HDS --form` print it; the keys are the
|
|
246
|
+
* JSON contract. Numbers a report did not give stay null, and `status` is
|
|
247
|
+
* the disclosed stake, null when the report does not say.
|
|
248
|
+
*/
|
|
249
|
+
export function beneficialReportRow(row: BeneficialOwnerRow) {
|
|
250
|
+
const { filing } = row;
|
|
251
|
+
return {
|
|
252
|
+
filer: filing.filerName,
|
|
253
|
+
filerCik: filing.filerCik,
|
|
254
|
+
form: row.formLabel,
|
|
255
|
+
formType: filing.form,
|
|
256
|
+
kind: filing.kind,
|
|
257
|
+
amendment: filing.amendment,
|
|
258
|
+
amendmentNo: filing.amendmentNo,
|
|
259
|
+
percentOfClass: filing.percentOfClass,
|
|
260
|
+
previousPercent: filing.previousPercent ?? null,
|
|
261
|
+
changePoints: row.changePoints,
|
|
262
|
+
shares: filing.shares,
|
|
263
|
+
classTitle: filing.classTitle,
|
|
264
|
+
cusip: filing.cusip,
|
|
265
|
+
eventDate: filing.eventDate,
|
|
266
|
+
filingDate: filing.filingDate,
|
|
267
|
+
previousFilingDate: filing.previousFilingDate ?? null,
|
|
268
|
+
thirteenF: row.thirteenF?.label ?? null,
|
|
269
|
+
status: row.stake,
|
|
270
|
+
/** The route could not read every filing, so this may not be the filer's latest disclosure or its change may be off. */
|
|
271
|
+
provisional: filing.provisional === true,
|
|
272
|
+
parsedFrom: filing.source,
|
|
273
|
+
accessionNumber: filing.accessionNumber,
|
|
274
|
+
filingUrl: filing.filingUrl,
|
|
275
|
+
reportingPersons: filing.reportingPersons,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export type BeneficialReportRow = ReturnType<typeof beneficialReportRow>;
|