@gloom-sh/terminal 0.19.1 → 0.19.3
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 +7 -1
- package/package.json +2 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +58 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app/runtime/startup.ts +3 -1
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/chart-series.ts +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +101 -27
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +14 -10
- package/src/cli/commands/search.ts +84 -25
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +6 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/glossary.ts +2 -1
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +189 -11
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +51 -5
- package/src/cli/listing-arg.ts +63 -10
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +49 -6
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +51 -6
- package/src/components/command-bar/selection-runtime.ts +12 -29
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +10 -5
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +8 -2
- package/src/components/layout/pane/footer/model.ts +73 -6
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +2 -1
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +4 -2
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +60 -26
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/display.tsx +16 -5
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +16 -3
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/plugin-cache.ts +15 -0
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +165 -90
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +324 -0
- package/src/data/sqlite/checkpointer.ts +285 -0
- package/src/data/sqlite/database.ts +80 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/market-data/realized-volatility.ts +4 -2
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/auto-update-node.ts +10 -6
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/client.ts +4 -1
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +5 -4
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/series.ts +3 -4
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +6 -4
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debt-maturities/series.ts +3 -3
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +19 -11
- package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ/detail-view.tsx +16 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fear-greed/series.ts +6 -4
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +19 -3
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/client.ts +4 -1
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/client.ts +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +16 -5
- package/src/plugins/builtin/market-movers/screener.ts +23 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/money-markets/series.ts +3 -5
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/gas-client.ts +2 -1
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/power/prices-client.ts +3 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/risk-factors/data.ts +8 -4
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/scanner/lock-note.ts +26 -3
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +127 -29
- package/src/plugins/builtin/thirteenf/data.ts +165 -35
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
- package/src/plugins/builtin/thirteenf/types.ts +14 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
- package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +6 -2
- package/src/plugins/builtin/volatility/series.ts +2 -1
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/client.ts +4 -6
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +64 -42
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +5 -2
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +6 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/electrobun/view/ui-host.tsx +9 -2
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/resolve.ts +2 -1
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +12 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +25 -9
- package/src/telemetry/stall-reports.ts +218 -13
- package/src/test-support/outage.ts +8 -0
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name-listing.ts +59 -29
- package/src/tickers/company-name.ts +84 -1
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +104 -8
- package/src/tickers/search/index.ts +5 -2
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/market.ts +5 -3
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +75 -8
- package/src/time-series/resolve.ts +32 -4
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/time-series/types.ts +7 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +40 -3
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +17 -1
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +17 -5
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +122 -5
- package/src/utils/perf-marks.ts +58 -12
- package/src/utils/price-history-integrity.ts +47 -1
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/time-series/studies.ts +0 -1628
|
@@ -4,6 +4,9 @@ import type { DataProvider } from "../../../types/data-provider";
|
|
|
4
4
|
import { isNoSourceAnswer } from "../../../sources/provider-errors";
|
|
5
5
|
import type { PricePoint } from "../../../types/financials";
|
|
6
6
|
import { logFailure } from "../../../utils/log-failure";
|
|
7
|
+
import { coalescedBump } from "./coalesced-bump";
|
|
8
|
+
import { readsInBatches } from "../../../market-data/batched-reads";
|
|
9
|
+
import { boardReadGate } from "./board-reads";
|
|
7
10
|
import type { BoardQuoteMap } from "../shared/use-quote-board";
|
|
8
11
|
import type { FuturesContract } from "../../../market-data/futures-board";
|
|
9
12
|
import {
|
|
@@ -33,8 +36,6 @@ const HISTORY_TTL_MS = 30 * 60_000;
|
|
|
33
36
|
* once a minute, not once a tick.
|
|
34
37
|
*/
|
|
35
38
|
const HISTORY_RETRY_MS = 60_000;
|
|
36
|
-
/** Forty-odd contracts open at once; a few requests at a time keep the board's quotes first. */
|
|
37
|
-
const HISTORY_CONCURRENCY = 4;
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Null when the source has no history for the contract (delisted, unknown),
|
|
@@ -43,16 +44,17 @@ const HISTORY_CONCURRENCY = 4;
|
|
|
43
44
|
*/
|
|
44
45
|
const historyCache = createTtlCache<readonly PricePoint[] | null>({ ttlMs: HISTORY_TTL_MS, rejectionMs: HISTORY_RETRY_MS });
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
/** `batch`: asked with every other row's, from a source that batches them (`DataProvider.batchesReads`). */
|
|
48
|
+
function readHistory(provider: DataProvider, symbol: string, batch: boolean): Promise<readonly PricePoint[] | null> {
|
|
49
|
+
return provider.getPriceHistory(symbol, "", "1Y", batch ? { batch } : undefined).then((points) => points.length > 0 ? points : null, (error: unknown) => {
|
|
48
50
|
if (isNoSourceAnswer(error)) return null;
|
|
49
51
|
throw error;
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
/** Whether the contract has an answer; false when the read failed. */
|
|
54
|
-
function loadHistory(provider: DataProvider, symbol: string): Promise<boolean> {
|
|
55
|
-
return historyCache.load(symbol, () => readHistory(provider, symbol))
|
|
56
|
+
function loadHistory(provider: DataProvider, symbol: string, batch: boolean): Promise<boolean> {
|
|
57
|
+
return historyCache.load(symbol, () => readHistory(provider, symbol, batch))
|
|
56
58
|
.then(() => true, logFailure("futures", "Read a futures contract's history failed", false));
|
|
57
59
|
}
|
|
58
60
|
|
|
@@ -89,18 +91,22 @@ export function useFrontContractReturns(
|
|
|
89
91
|
let cancelled = false;
|
|
90
92
|
let failed = false;
|
|
91
93
|
let retryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
const landed = coalescedBump(() => {
|
|
95
|
+
if (!cancelled) setVersion((current) => current + 1);
|
|
96
|
+
});
|
|
97
|
+
// Twelve reads at a time, shared with the rows' terms (`boardRead`), or all at once from a source that
|
|
98
|
+
// batches them (`DataProvider.batchesReads`); a board that moved on skips its turn.
|
|
99
|
+
void readsInBatches(provider, "getPriceHistory").then((batch) => Promise.all(queue.map((symbol) => boardReadGate(batch)(async () => {
|
|
100
|
+
if (cancelled) return;
|
|
101
|
+
if (!await loadHistory(provider, symbol, batch)) failed = true;
|
|
102
|
+
landed.bump();
|
|
103
|
+
})))).then(() => {
|
|
104
|
+
landed.flush();
|
|
99
105
|
if (failed && !cancelled) retryTimer = setTimeout(() => setRetry((current) => current + 1), HISTORY_RETRY_MS);
|
|
100
106
|
});
|
|
101
107
|
return () => {
|
|
102
108
|
cancelled = true;
|
|
103
|
-
|
|
109
|
+
landed.cancel();
|
|
104
110
|
clearTimeout(retryTimer);
|
|
105
111
|
};
|
|
106
112
|
}, [candidates, enabled, provider, today, retry]);
|
|
@@ -7,6 +7,7 @@ import { newestReportTime, oldestReportTime } from "../../../utils/utc-time";
|
|
|
7
7
|
import { fetchFuturesCurve, loadCurveSpot, loadCurveSpotBars, loadFuturesCurveAsOf } from "./client";
|
|
8
8
|
import { basisSpotSymbol, BASIS_CONVENTION, blankBasisNote, catalogueCurveUnit, curveAsOfDate, curveBasisPercent, curveBasisRows, curveChangeText, curveContractCode, curvePrice, curveRoundedPrice, curveSpotSource, curveSpreadLabel, curveTimestamp, curveNoTradeSince, curveUnitLabel, noTradeChangeNote, normalizeCurveRoot, shortDatedBasisNote, thinSpreadNotice, unpairedContractCount, unsupportedCurveRootMessage, type ContractBasis } from "./model";
|
|
9
9
|
import { COMMODITIES_VIEW_STATE } from "../commodities-energy/view-state";
|
|
10
|
+
import { knownGapMessage } from "../../../cli/known-gaps";
|
|
10
11
|
|
|
11
12
|
/** The root a report shows when none is given. */
|
|
12
13
|
const DEFAULT_ROOT = "ES";
|
|
@@ -120,7 +121,11 @@ export const futuresCurveHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
120
121
|
async load(args, ctx) {
|
|
121
122
|
const input = args.argument || DEFAULT_ROOT;
|
|
122
123
|
const root = normalizeCurveRoot(input);
|
|
123
|
-
if (!root)
|
|
124
|
+
if (!root) {
|
|
125
|
+
// A subject no source carries (`fn CTM BDI`, the Baltic Dry Index) is said with its proxies, as search says it.
|
|
126
|
+
const gap = typeof input === "string" ? knownGapMessage(input) : null;
|
|
127
|
+
throw Object.assign(new Error(unsupportedCurveRootMessage(input)), gap ? { details: gap } : {});
|
|
128
|
+
}
|
|
124
129
|
const date = curveAsOfDate(args.options.date, new Date(), "--date");
|
|
125
130
|
// The archive keeps no spot history, so a past curve has no basis.
|
|
126
131
|
const spotSymbol = date ? null : basisSpotSymbol(root);
|
|
@@ -2,10 +2,9 @@ import type { PluginModule } from "../plugin-module";
|
|
|
2
2
|
import { futuresCurveCache } from "./client";
|
|
3
3
|
import { futuresCurveHeadless } from "./headless";
|
|
4
4
|
import { BASIS_FORMULA, CURVE_HORIZONS, CURVE_ROOTS, curveRootForTicker, curveTitle, DEFAULT_CURVE_HORIZON, normalizeCurveRoot } from "./model";
|
|
5
|
-
import { FuturesCurvePane } from "./pane";
|
|
6
5
|
|
|
7
6
|
export const futuresCurveModule: PluginModule = {
|
|
8
|
-
panes: [{ id: "futures-curve", name: "Futures Curve", icon: "F", component: FuturesCurvePane,
|
|
7
|
+
panes: [{ id: "futures-curve", name: "Futures Curve", icon: "F", get component(): typeof import("./pane").FuturesCurvePane { const { FuturesCurvePane } = require("./pane"); return FuturesCurvePane; },
|
|
9
8
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 98, height: 30 },
|
|
10
9
|
tableExport: true, headless: futuresCurveHeadless,
|
|
11
10
|
settings: (context) => ({ title: "Futures Curve Settings",
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type PaneFooterSegment,
|
|
10
10
|
} from "../../../components";
|
|
11
11
|
import { handleRefreshKey } from "../../../components/data-table/table-pane";
|
|
12
|
-
import { savedCopyFooterSegment } from "../../../components/layout/pane/footer";
|
|
12
|
+
import { ageFooterSegment, savedCopyFooterSegment } from "../../../components/layout/pane/footer";
|
|
13
13
|
import { CountryFlag } from "../../../components/ui/country-flag";
|
|
14
14
|
import { getSharedMarketDataCoordinator } from "../../../market-data/coordinator";
|
|
15
15
|
import { useFxRatesMap } from "../../../market-data/hooks";
|
|
@@ -244,7 +244,7 @@ function FxMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
244
244
|
if (status.loading > 0) info.push({ id: "loading", parts: [{ text: "loading", tone: "muted" }] });
|
|
245
245
|
if (statusText) info.push({ id: "rates", parts: [{ text: statusText, tone: status.stale || status.unknownTime || status.unavailable ? "warning" : "muted" }] });
|
|
246
246
|
if (savedAt != null) info.push(savedCopyFooterSegment("updated", savedAt));
|
|
247
|
-
else if (updatedAgo) info.push(
|
|
247
|
+
else if (updatedAgo) info.push(ageFooterSegment(updatedAgo, { verb: "fetched" }));
|
|
248
248
|
return { info };
|
|
249
249
|
}, [savedAt, status.loading, statusText, updatedAgo]);
|
|
250
250
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useMemo } from "react";
|
|
2
|
-
import type { GpuBoardRow } from "../../../api-client/gpu";
|
|
2
|
+
import type { GpuBoardRow, GpuSettle } from "../../../api-client/gpu";
|
|
3
3
|
import {
|
|
4
4
|
Badge, buildSectionedRows, ChartTableHeader, DataTableView, EMPTY_TABLE_CELL, isSectionedItemRow, QueryBar,
|
|
5
5
|
renderSectionedRowHeader, type DataTableCell, type DataTableColumn, type SectionedRow,
|
|
@@ -12,11 +12,11 @@ import { Box, Text, TextAttributes, useUiCapabilities } from "../../../ui";
|
|
|
12
12
|
import { loadGpuHistory } from "./client";
|
|
13
13
|
import { GpuPriceLadder } from "./ladder";
|
|
14
14
|
import {
|
|
15
|
-
gpuAvailability, gpuBoardSections, gpuChange, gpuChangeColor, gpuChangesLocked, gpuChangeWindows, gpuHeadline, gpuPrice, gpuPriceLadder,
|
|
16
|
-
type GpuChangeKey,
|
|
15
|
+
GPU_SETTLE_SECTION, gpuAvailability, gpuBoardSections, gpuChange, gpuChangeColor, gpuChangesLocked, gpuChangeWindows, gpuHeadline, gpuPrice, gpuPriceLadder,
|
|
16
|
+
gpuSettleRows, gpuShortSource, gpuSparklineSeries, gpuTime, gpuVariant, isGpuSettleRow, type GpuBoardItem, type GpuChangeKey,
|
|
17
17
|
} from "./model";
|
|
18
18
|
|
|
19
|
-
type BoardRow = SectionedRow<
|
|
19
|
+
type BoardRow = SectionedRow<GpuBoardItem>;
|
|
20
20
|
const rowKey = (row: BoardRow) => row.key;
|
|
21
21
|
const EMPTY_HISTORY = new Map<string, PricePoint[]>();
|
|
22
22
|
const CHANGE_LABELS: Record<GpuChangeKey, string> = { change1d: "1D", change7d: "7D", change30d: "30D" };
|
|
@@ -79,21 +79,29 @@ function boardColumns(width: number, options: { windows: GpuChangeKey[]; trend:
|
|
|
79
79
|
];
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
export function GpuBoard({ rows, models, asOf, model, setModel, selectedId, select, emptyTitle, width, height, focused, pending = false }: {
|
|
83
|
-
rows: GpuBoardRow[]; models: readonly string[]; asOf: string | null; model: string; setModel: (value: string) => void; selectedId: string;
|
|
82
|
+
export function GpuBoard({ rows, settles, models, asOf, model, setModel, selectedId, select, emptyTitle, width, height, focused, pending = false }: {
|
|
83
|
+
rows: GpuBoardRow[]; settles?: readonly GpuSettle[]; models: readonly string[]; asOf: string | null; model: string; setModel: (value: string) => void; selectedId: string;
|
|
84
|
+
/** Opening a settle row selects it: its history is the GPU pages', not the History tab's. */
|
|
85
|
+
select: (row: GpuBoardItem, open?: boolean) => void;
|
|
84
86
|
emptyTitle: string; width: number; height: number; focused: boolean;
|
|
85
87
|
/** The picked GPU's free preview is still loading. */
|
|
86
88
|
pending?: boolean;
|
|
87
89
|
}) {
|
|
88
90
|
const colors = useThemeColors();
|
|
89
91
|
const sections = useMemo(() => gpuBoardSections(rows, model), [rows, model]);
|
|
90
|
-
|
|
92
|
+
// A GPU nobody quotes has no board to lead.
|
|
93
|
+
const settleRows = useMemo(() => sections.length ? gpuSettleRows(settles, model) : [], [sections, settles, model]);
|
|
94
|
+
const items = useMemo(() => buildSectionedRows<GpuBoardItem>([
|
|
95
|
+
...(settleRows.length ? [{ label: GPU_SETTLE_SECTION, items: settleRows }] : []),
|
|
96
|
+
...sections.map((section) => ({ label: section.label, items: section.rows })),
|
|
97
|
+
], (row) => row.id), [settleRows, sections]);
|
|
91
98
|
const visible = useMemo(() => sections.flatMap((section) => section.rows), [sections]);
|
|
92
|
-
const windows = useMemo(() => gpuChangeWindows(visible), [visible]);
|
|
99
|
+
const windows = useMemo(() => gpuChangeWindows([...visible, ...settleRows.map((row) => row.settle)]), [visible, settleRows]);
|
|
93
100
|
const history = useHeadlineHistory(rows, model);
|
|
94
101
|
const trend = [...history.values()].some((points) => points.length >= MIN_TREND_POINTS);
|
|
95
102
|
const ladder = useMemo(() => model ? gpuPriceLadder(rows, model) : [], [rows, model]);
|
|
96
|
-
const
|
|
103
|
+
const selectedSettle = settleRows.find((row) => row.id === selectedId) ?? null;
|
|
104
|
+
const selected = selectedSettle ? null : visible.find((row) => row.id === selectedId) ?? visible[0] ?? null;
|
|
97
105
|
const medium = width >= 100;
|
|
98
106
|
const asOfDay = asOf?.slice(0, 10) ?? null;
|
|
99
107
|
const columns = boardColumns(width, { windows, trend, range: visible.some((row) => row.stats), availability: visible.some((row) => row.availability) });
|
|
@@ -102,9 +110,10 @@ export function GpuBoard({ rows, models, asOf, model, setModel, selectedId, sele
|
|
|
102
110
|
|
|
103
111
|
const renderCell = (item: BoardRow, column: DataTableColumn, _index: number, state: { selected: boolean }): DataTableCell => {
|
|
104
112
|
if (!isSectionedItemRow(item)) return EMPTY_TABLE_CELL;
|
|
113
|
+
const ink = state.selected ? colors.selectedText : undefined;
|
|
114
|
+
if (isGpuSettleRow(item.item)) return settleCell(item.item.settle, column, ink);
|
|
105
115
|
const row = item.item;
|
|
106
116
|
const headline = gpuHeadline(row);
|
|
107
|
-
const ink = state.selected ? colors.selectedText : undefined;
|
|
108
117
|
switch (column.id) {
|
|
109
118
|
case "source": {
|
|
110
119
|
const name = gpuShortSource(row);
|
|
@@ -153,6 +162,36 @@ export function GpuBoard({ rows, models, asOf, model, setModel, selectedId, sele
|
|
|
153
162
|
}
|
|
154
163
|
};
|
|
155
164
|
|
|
165
|
+
/**
|
|
166
|
+
* A settle draws as the board's lead rows do (bold name and price), on the
|
|
167
|
+
* same columns: its moves are against earlier settles, and its time is the
|
|
168
|
+
* settle time.
|
|
169
|
+
*/
|
|
170
|
+
const settleCell = (settle: GpuSettle, column: DataTableColumn, ink: string | undefined): DataTableCell => {
|
|
171
|
+
const settled = settle.status === "settled" && settle.pricePerGpuHr != null;
|
|
172
|
+
switch (column.id) {
|
|
173
|
+
case "source": {
|
|
174
|
+
const name = `Gloom ${settle.gpuModel}`;
|
|
175
|
+
const note = settle.kind === "correction" ? "corrected" : settled ? "settled" : "no settle";
|
|
176
|
+
return { text: `${name} ${note}`, content: <Box flexDirection="row" height={1} overflow="hidden">
|
|
177
|
+
<Text fg={ink ?? colors.textBright} attributes={TextAttributes.BOLD}>{name}</Text>
|
|
178
|
+
<Text fg={ink ?? colors.textDim}>{` ${note}`}</Text>
|
|
179
|
+
</Box> };
|
|
180
|
+
}
|
|
181
|
+
case "variant": return { text: gpuVariant(settle).join(" "), content: <GpuVariantChips row={settle} width={column.width} selected={!!ink} /> };
|
|
182
|
+
case "price": return settled ? { text: gpuPrice(settle.pricePerGpuHr), value: settle.pricePerGpuHr, color: colors.textBright, attributes: TextAttributes.BOLD }
|
|
183
|
+
: { text: "-", value: null, color: colors.textMuted };
|
|
184
|
+
case "unavailableChange": return { text: "-", value: null, color: colors.textMuted };
|
|
185
|
+
case "change1d": case "change7d": case "change30d": {
|
|
186
|
+
const value = settle[column.id];
|
|
187
|
+
return value == null ? { text: "-", value: null, color: colors.textMuted } : { text: gpuChange(value), value, color: gpuChangeColor(value, colors) };
|
|
188
|
+
}
|
|
189
|
+
case "asof": return { text: asOfDay && settle.settleAt.startsWith(asOfDay) ? settle.settleAt.slice(11, 16) : gpuTime(settle.settleAt, true),
|
|
190
|
+
value: settle.settleAt, color: colors.textDim };
|
|
191
|
+
default: return EMPTY_TABLE_CELL;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
156
195
|
const query = <GpuModelQuery models={models} model={model} setModel={setModel} width={width} />;
|
|
157
196
|
return <DataTableView<BoardRow> columns={columns} items={items} rootWidth={width} rootHeight={height} focused={focused}
|
|
158
197
|
status={pending && !items.length ? { loading: true, subject: "GPU rental prices" } : undefined}
|
|
@@ -161,9 +200,9 @@ export function GpuBoard({ rows, models, asOf, model, setModel, selectedId, sele
|
|
|
161
200
|
render: (size) => <GpuPriceLadder rows={ladder} width={size.width} height={size.height}
|
|
162
201
|
selected={selected && selected.gpuModel === model && selected.providerClass !== "aggregate" ? { basis: selected.basis, price: selected.pricePerGpuHr } : null} /> }} />
|
|
163
202
|
: query}
|
|
164
|
-
selection={{ kind: "id", selectedId: selected?.id ?? null, getId: rowKey,
|
|
203
|
+
selection={{ kind: "id", selectedId: selectedSettle?.id ?? selected?.id ?? null, getId: rowKey,
|
|
165
204
|
onChange: (_id, row) => { if (row && isSectionedItemRow(row)) select(row.item); } }}
|
|
166
205
|
isNavigable={isSectionedItemRow} renderSectionHeader={renderSectionedRowHeader}
|
|
167
|
-
getItemKey={rowKey} onActivate={(row) => { if (isSectionedItemRow(row)) select(row.item,
|
|
206
|
+
getItemKey={rowKey} onActivate={(row) => { if (isSectionedItemRow(row)) select(row.item, !isGpuSettleRow(row.item)); }} sortColumnId={null} sortDirection="asc"
|
|
168
207
|
selectedTextOverridesCellColor renderCell={renderCell} emptyStateTitle={emptyTitle} />;
|
|
169
208
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolvePlanAccess } from "../../../api-client/plan-rules";
|
|
2
|
+
import { sourceDidNotAnswer } from "../../../api-client/errors";
|
|
2
3
|
import { apiClient } from "../../../api-client";
|
|
3
|
-
import type { GpuBoardPayload, GpuEventsPayload, GpuHistoryPayload, GpuHistoryQuery, GpuObservation } from "../../../api-client/gpu";
|
|
4
|
+
import type { GpuBoardPayload, GpuEventsPayload, GpuHistoryPayload, GpuHistoryQuery, GpuObservation, GpuSettle } from "../../../api-client/gpu";
|
|
4
5
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
5
6
|
import type { DataProvider } from "../../../types/data-provider";
|
|
6
7
|
import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
@@ -58,6 +59,17 @@ function validObservation(row: unknown): row is GpuObservation {
|
|
|
58
59
|
&& (stats.constituentPrices === undefined || record(stats.constituentPrices) && Object.values(stats.constituentPrices).every((value) => finite(value) && value > 0)));
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
function validSettle(row: unknown): row is GpuSettle {
|
|
63
|
+
if (!record(row)) return false;
|
|
64
|
+
const settled = row.status === "settled";
|
|
65
|
+
return [row.id, row.gpuModel, row.formFactor, row.label].every((value) => typeof value === "string" && value.length > 0)
|
|
66
|
+
&& typeof row.date === "string" && /^\d{4}-\d{2}-\d{2}$/.test(row.date) && instant(row.settleAt) && instant(row.publishedAt)
|
|
67
|
+
&& (row.memoryGb === null || finite(row.memoryGb) && row.memoryGb > 0)
|
|
68
|
+
&& (settled ? finite(row.pricePerGpuHr) && row.pricePerGpuHr > 0 : row.status === "no-settle" && row.pricePerGpuHr === null)
|
|
69
|
+
&& row.currency === "USD" && ["settle", "correction"].includes(row.kind) && nullableText(row.corrects)
|
|
70
|
+
&& [row.change1d, row.change7d, row.change30d].every(nullableNumber);
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
function validateBoard(payload: GpuBoardPayload): GpuBoardPayload {
|
|
62
74
|
if (!record(payload) || !validAccess(payload.access) || !instant(payload.generatedAt) || !nullableInstant(payload.asOf) || typeof payload.stale !== "boolean"
|
|
63
75
|
|| !["available", "partial", "unavailable"].includes(payload.status) || !strings(payload.gaps)
|
|
@@ -65,7 +77,9 @@ function validateBoard(payload: GpuBoardPayload): GpuBoardPayload {
|
|
|
65
77
|
&& typeof row.id === "string" && row.id === `${row.source}:${row.skuKey}` && typeof row.label === "string" && typeof row.sourceLabel === "string"
|
|
66
78
|
&& [row.change1d, row.change7d, row.change30d].every(nullableNumber) && typeof row.stale === "boolean" && nullableText(row.lastError))
|
|
67
79
|
|| new Set(payload.rows.map((row) => row.id)).size !== payload.rows.length) throw new Error("The server returned invalid GPU rental prices.");
|
|
68
|
-
|
|
80
|
+
// The settles ride along with the board: one that does not read right is left out, never the board with it.
|
|
81
|
+
return payload.settles === undefined ? payload
|
|
82
|
+
: { ...payload, settles: Array.isArray(payload.settles) ? payload.settles.filter(validSettle) : [] };
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
function validateHistory(payload: GpuHistoryPayload): GpuHistoryPayload {
|
|
@@ -94,7 +108,9 @@ export async function fetchGpuBoard(client: Pick<GpuApi, "getCloudGpuBoard"> = a
|
|
|
94
108
|
try { payload = await client.getCloudGpuBoard(); }
|
|
95
109
|
catch (error) { throw unavailableOnServer(error, GPU_NOT_AVAILABLE, [404, 503]); }
|
|
96
110
|
const board = validateBoard(payload);
|
|
97
|
-
|
|
111
|
+
// The server's source did not answer this time: a saved copy answers, as it does a 5xx.
|
|
112
|
+
if (board.status === "unavailable") throw sourceDidNotAnswer(GPU_NOT_AVAILABLE);
|
|
113
|
+
if (!board.rows.length) throw new Error(GPU_NOT_AVAILABLE);
|
|
98
114
|
return board;
|
|
99
115
|
}
|
|
100
116
|
|
|
@@ -14,7 +14,8 @@ import { Box, TextAttributes, useUiCapabilities } from "../../../ui";
|
|
|
14
14
|
import { ClippedText } from "../../../components/ui/display";
|
|
15
15
|
import { publicTickerKey } from "../../../utils/exchanges";
|
|
16
16
|
import { displayWidth } from "../../../utils/format";
|
|
17
|
-
import { quoteBoardFooterInfo, quoteBoardStatus, renderQuoteBoardCell, useQuoteBoard } from "../shared/use-quote-board";
|
|
17
|
+
import { boardQuotes, quoteBoardFooterInfo, quoteBoardStatus, renderQuoteBoardCell, useQuoteBoard } from "../shared/use-quote-board";
|
|
18
|
+
import { useQuoteDelayFooter } from "../shared/cloud-upgrade";
|
|
18
19
|
import { GpuModelQuery } from "./board";
|
|
19
20
|
import { loadGpuEquityHistory } from "./client";
|
|
20
21
|
import { GPU_EQUITIES, GPU_EQUITY_GROUPS, gpuChange, gpuChangeColor, gpuChangesLocked, gpuEquityGap, gpuEquityRows, gpuPrice, gpuShortSource, gpuVariant, type GpuEquityRow } from "./model";
|
|
@@ -62,8 +63,10 @@ export function GpuEquities({ board, models, locked, model, setModel, reloadBoar
|
|
|
62
63
|
items: rows.filter((row) => (group.roles as readonly string[]).includes(row.role)) })), (row) => row.symbol), [rows]);
|
|
63
64
|
const historyMap = useMemo(() => new Map(history.data?.map((entry) => [entry.symbol, entry]) ?? []), [history.data]);
|
|
64
65
|
const ends = [...new Set(history.data?.flatMap((entry) => entry.asOf ? [entry.asOf] : []) ?? [])].sort();
|
|
66
|
+
const quoteDelay = useQuoteDelayFooter(boardQuotes(quotes), "gpu-equities-delay");
|
|
65
67
|
usePaneFooter("gpu:equity-status", () => ({ info: [...quoteBoardFooterInfo(quoteBoardStatus(quotes)),
|
|
66
|
-
...(ends.length === 1 ? [{ id: "five-asof", parts: [{ text: `5D closes through ${ends[0]}`, tone: "muted" as const }] }] : [])
|
|
68
|
+
...(ends.length === 1 ? [{ id: "five-asof", parts: [{ text: `5D closes through ${ends[0]}`, tone: "muted" as const }] }] : []),
|
|
69
|
+
...(quoteDelay ? [quoteDelay] : [])] }), [quotes, quoteDelay, ends.join(",")]);
|
|
67
70
|
usePaneNoticeFooter({ registrationId: "gpu:equity-notices", focused,
|
|
68
71
|
notices: [...(history.error ? [history.error] : []), ...(history.data?.flatMap((entry) => entry.error ? [entry.error] : []) ?? [])] });
|
|
69
72
|
const dataRef = useRef({ quotes, historyMap });
|
|
@@ -4,7 +4,7 @@ import { formatUtcTime, parseReportTime } from "../../../utils/utc-time";
|
|
|
4
4
|
import { fetchGpuBoard, fetchGpuEvents, fetchGpuHistory, fetchGpuModelPreviews, gpuPreviewModels, loadGpuEquityHistory } from "./client";
|
|
5
5
|
import {
|
|
6
6
|
GPU_TABS, gpuBasisLabel, gpuChange, gpuChangesLocked, gpuEquityGap, gpuEquityModels, gpuEquityRows, gpuEventDate, gpuFamilies, gpuFamily,
|
|
7
|
-
gpuLabel, gpuNoMatch, gpuPrice, gpuProvenanceLabel, gpuRows, gpuSource, gpuTab,
|
|
7
|
+
gpuLabel, gpuNoMatch, gpuPrice, gpuProvenanceLabel, gpuRows, gpuSettleRows, gpuSource, gpuTab, GPU_SETTLE_SECTION,
|
|
8
8
|
} from "./model";
|
|
9
9
|
import { quoteFreshnessFields } from "../shared/report-freshness";
|
|
10
10
|
import { countOf, freePreviewNote } from "../shared/free-preview";
|
|
@@ -33,6 +33,13 @@ const columns = [
|
|
|
33
33
|
{ key: "observedAt", header: "As of", format: minute },
|
|
34
34
|
];
|
|
35
35
|
|
|
36
|
+
const settleColumns = [
|
|
37
|
+
{ key: "gpu", header: "GPU" }, { key: "status", header: "Settle" },
|
|
38
|
+
{ key: "price", header: "$/GPU-hr", align: "right" as const, format: price },
|
|
39
|
+
{ key: "change1d", header: "1D", align: "right" as const, format: percent }, { key: "change7d", header: "7D", align: "right" as const, format: percent },
|
|
40
|
+
{ key: "change30d", header: "30D", align: "right" as const, format: percent }, { key: "settleAt", header: "Settled", format: minute },
|
|
41
|
+
];
|
|
42
|
+
|
|
36
43
|
export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
37
44
|
// Observed rental prices; the Equities tab's quotes keep their market line.
|
|
38
45
|
shape: "bundle", freshness: { dataset: true }, argument: { kind: "free-text", optional: true, placeholder: "GPU", description: "GPU model, such as H100, h-100 or B200." },
|
|
@@ -41,6 +48,7 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
41
48
|
limitations: ["List prices, provider-declared spot and asks are distinct bases.",
|
|
42
49
|
"Hyperscaler and Neocloud rows are list indexes: the median of the providers' list prices linked over time, so a provider joining or leaving causes no jump; sample.rawMedian is the raw median of today's providers and can differ slightly.",
|
|
43
50
|
"1D/7D/30D need sufficient observation history; unavailable changes remain null.",
|
|
51
|
+
"The Gloom daily settle is Gloom's official price per GPU and form factor, set at 20:00 UTC and never revised; a day with too few prices has no settle.",
|
|
44
52
|
"Free preview: three board rows, or the latest rows of the GPU asked for; Pro unlocks every provider, the moves and the history."] },
|
|
45
53
|
describe: "GPU rental prices in USD per GPU-hour",
|
|
46
54
|
async load(args, ctx) {
|
|
@@ -113,7 +121,11 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
113
121
|
}
|
|
114
122
|
const boardRows = gpuRows(rows, model).map((row) => ({ ...observationRow(row), change1d: row.change1d, change7d: row.change7d,
|
|
115
123
|
change30d: row.change30d, stale: row.stale, sample: row.stats ?? null, ...(gpuChangesLocked(row) ? { changesLocked: true } : {}) }));
|
|
116
|
-
|
|
124
|
+
const settles = gpuSettleRows(board.settles, model).map(({ settle }) => ({ gpu: gpuLabel(settle), status: settle.status === "settled" ? "settled" : "no settle",
|
|
125
|
+
price: settle.pricePerGpuHr, change1d: settle.change1d, change7d: settle.change7d, change30d: settle.change30d, settleAt: settle.settleAt,
|
|
126
|
+
date: settle.date, ...(settle.kind === "correction" ? { corrected: true } : {}) }));
|
|
127
|
+
return { sections: [...(settles.length ? [{ title: GPU_SETTLE_SECTION, columns: settleColumns, rows: settles }] : []),
|
|
128
|
+
{ title: "GPU rental prices", columns, rows: boardRows, empty: `No ${model ? `${model} ` : ""}rental prices have been observed yet.` }],
|
|
117
129
|
freshness, complete: !board.access?.locked, metadata, errors: [...locks, ...board.gaps] };
|
|
118
130
|
},
|
|
119
131
|
};
|
|
@@ -2,11 +2,10 @@ import type { PluginModule } from "../plugin-module";
|
|
|
2
2
|
import { gpuBoardCache, gpuEventsCache, gpuHistoryCache } from "./client";
|
|
3
3
|
import { gpuHeadless } from "./headless";
|
|
4
4
|
import { GPU_MODELS, gpuArgument } from "./model";
|
|
5
|
-
import { GpuPane } from "./pane";
|
|
6
5
|
import { GPU_SERIES } from "./series";
|
|
7
6
|
|
|
8
7
|
export const gpuModule: PluginModule = {
|
|
9
|
-
panes: [{ id: "gpu", name: "GPU Rental Prices", icon: "G", component: GpuPane, defaultPosition: "right", defaultMode: "floating",
|
|
8
|
+
panes: [{ id: "gpu", name: "GPU Rental Prices", icon: "G", get component(): typeof import("./pane").GpuPane { const { GpuPane } = require("./pane"); return GpuPane; }, defaultPosition: "right", defaultMode: "floating",
|
|
10
9
|
defaultFloatingSize: { width: 120, height: 34 }, tableExport: true, headless: gpuHeadless }],
|
|
11
10
|
paneTemplates: [{ id: "gpu-pane", paneId: "gpu", label: "GPU Rental Prices",
|
|
12
11
|
description: "Pro GPU rental list prices, provider-declared spot and marketplace asks, with sourced price history and related equities. Free preview available.",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GpuBasis, GpuBoardRow, GpuEvent, GpuObservation } from "../../../api-client/gpu";
|
|
1
|
+
import type { GpuBasis, GpuBoardRow, GpuEvent, GpuObservation, GpuSettle } from "../../../api-client/gpu";
|
|
2
2
|
import { staticSeries } from "../../../components/chart/static/series";
|
|
3
3
|
import { SERIES_COLORS } from "../../../theme/series-colors";
|
|
4
4
|
import type { ResolvedSeries } from "../../../time-series/types";
|
|
@@ -175,9 +175,27 @@ export function gpuBoardSections(rows: readonly GpuBoardRow[], model = ""): GpuB
|
|
|
175
175
|
return sections;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/** Gloom's daily settle as a board row: it leads the board, above every quote. */
|
|
179
|
+
export interface GpuSettleRow { kind: "settle"; id: string; settle: GpuSettle }
|
|
180
|
+
export type GpuBoardItem = GpuBoardRow | GpuSettleRow;
|
|
181
|
+
export const isGpuSettleRow = (row: GpuBoardItem): row is GpuSettleRow => (row as Partial<GpuSettleRow>).kind === "settle";
|
|
182
|
+
export const GPU_SETTLE_SECTION = "Gloom daily settle";
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The settle rows of the board: every form factor of the picked GPU, settled
|
|
186
|
+
* or not, so a day with too few prices says so; across all GPUs only the ones
|
|
187
|
+
* settled. GPUs in board order, SXM first.
|
|
188
|
+
*/
|
|
189
|
+
export function gpuSettleRows(settles: readonly GpuSettle[] | undefined, model = ""): GpuSettleRow[] {
|
|
190
|
+
return (settles ?? []).filter((settle) => model ? settle.gpuModel === model : settle.status === "settled")
|
|
191
|
+
.sort((a, b) => gpuModelOrder(a.gpuModel, b.gpuModel) || Number(b.formFactor === "SXM") - Number(a.formFactor === "SXM")
|
|
192
|
+
|| a.formFactor.localeCompare(b.formFactor))
|
|
193
|
+
.map((settle) => ({ kind: "settle", id: `settle:${settle.gpuModel}:${settle.formFactor}`, settle }));
|
|
194
|
+
}
|
|
195
|
+
|
|
178
196
|
export type GpuChangeKey = "change1d" | "change7d" | "change30d";
|
|
179
197
|
/** Change windows with at least one figure; missing windows remain unavailable. */
|
|
180
|
-
export const gpuChangeWindows = (rows: readonly GpuBoardRow[]): GpuChangeKey[] =>
|
|
198
|
+
export const gpuChangeWindows = (rows: readonly Pick<GpuBoardRow, GpuChangeKey>[]): GpuChangeKey[] =>
|
|
181
199
|
(["change1d", "change7d", "change30d"] as const).filter((key) => rows.some((row) => row[key] != null));
|
|
182
200
|
|
|
183
201
|
/**
|
|
@@ -71,7 +71,7 @@ function GpuView({ width, height, focused }: PaneProps) {
|
|
|
71
71
|
{strip}
|
|
72
72
|
<PaneStatusBody loading={resource.loading && !data} error={!data && !notAvailable ? resource.error : null}
|
|
73
73
|
empty={notAvailable} emptyTitle={GPU_NOT_AVAILABLE} subject="GPU rental prices">
|
|
74
|
-
{data ? tab === "board" ? <GpuBoard rows={rows} models={models} asOf={data.asOf} model={activeModel} setModel={setModel} selectedId={selectedId} emptyTitle={emptyTitle}
|
|
74
|
+
{data ? tab === "board" ? <GpuBoard rows={rows} settles={data.settles} models={models} asOf={data.asOf} model={activeModel} setModel={setModel} selectedId={selectedId} emptyTitle={emptyTitle}
|
|
75
75
|
select={(row, open) => { setSelectedId(row.id); if (open) setTab("history"); }} {...body} />
|
|
76
76
|
: tab === "history" ? <GpuHistory rows={rows} models={models} model={unknownModel ? "" : activeModel} setModel={setModel} selectedId={selectedId} onSelect={setSelectedId} reloadBoard={reloadBoard} {...body} />
|
|
77
77
|
: tab === "changes" ? <GpuChanges models={models} model={unknownModel ? "" : activeModel} setModel={setModel} reloadBoard={reloadBoard} {...body} />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SeriesDefinition } from "../../../series/types";
|
|
2
|
-
import {
|
|
2
|
+
import { cachedSeriesNote } from "../shared/series-warning";
|
|
3
3
|
import { loadGpuBoard, loadGpuHistory } from "./client";
|
|
4
4
|
import { GPU_MODELS, gpuListIndex } from "./model";
|
|
5
5
|
|
|
@@ -27,12 +27,11 @@ export const GPU_SERIES: readonly SeriesDefinition[] = GPU_MODELS.map((model): S
|
|
|
27
27
|
if (!index) throw new Error(`No ${model} list index is listed yet.`);
|
|
28
28
|
const history = await loadGpuHistory(index.id);
|
|
29
29
|
if (history.payload.access?.locked) throw new Error(GPU_PRO_ONLY);
|
|
30
|
-
const warning = cachedSeriesWarning(history);
|
|
31
30
|
return {
|
|
32
31
|
points: history.payload.points
|
|
33
32
|
.filter((point) => `${point.source}:${point.skuKey}` === index.id)
|
|
34
33
|
.map((point) => ({ date: point.observedAt, value: point.pricePerGpuHr })),
|
|
35
|
-
...(
|
|
34
|
+
...cachedSeriesNote(history),
|
|
36
35
|
};
|
|
37
36
|
},
|
|
38
37
|
}));
|
|
@@ -425,7 +425,7 @@ export const helpModule: PluginModule = {
|
|
|
425
425
|
name: "Help",
|
|
426
426
|
icon: "?",
|
|
427
427
|
component: HelpPane,
|
|
428
|
-
reportFreshness: { status: "not-a-feed", basis: "documentation" },
|
|
428
|
+
reportFreshness: { status: "not-a-feed", basis: "documentation", local: true },
|
|
429
429
|
defaultPosition: "right",
|
|
430
430
|
defaultMode: "floating",
|
|
431
431
|
defaultFloatingSize: { width: 88, height: 32 },
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "../../../components";
|
|
12
12
|
import { useCompanyDataFooter } from "../../../components/layout/pane/status-footer";
|
|
13
13
|
import { handleRefreshKey, loadingErrorFooterInfo } from "../../../components/data-table/table-pane";
|
|
14
|
+
import { STALE_SEGMENT } from "../../../components/layout/pane/status-footer";
|
|
14
15
|
import type { IssuerListingParams } from "../../../api-client/paths";
|
|
15
16
|
import { useAsyncResource } from "../../../react/async-resource";
|
|
16
17
|
import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
@@ -43,8 +44,6 @@ import {
|
|
|
43
44
|
import type { Holder13FMatch } from "./thirteenf-match";
|
|
44
45
|
import type { HolderRow } from "./types";
|
|
45
46
|
|
|
46
|
-
const STALE_SEGMENT = { id: "stale", parts: [{ text: "stale", tone: "warning" as const }] };
|
|
47
|
-
|
|
48
47
|
/**
|
|
49
48
|
* A table line: a filer's row, or the line beneath it giving the same shares
|
|
50
49
|
* against today's count when the report's base is no longer today's.
|
|
@@ -7,7 +7,7 @@ import { nonUsHolderCaveat } from "./report-header";
|
|
|
7
7
|
/**
|
|
8
8
|
* A US listing's 13F holders from the SEC 13F holder index, the list `fn 13F`
|
|
9
9
|
* pages through: every filer that reported the security for the latest
|
|
10
|
-
* quarter, largest first
|
|
10
|
+
* quarter, largest first. HDS shows them in the units of the
|
|
11
11
|
* source's own short list (each position at the latest price, the stake as a
|
|
12
12
|
* share of the class that list divides by), and keeps that short list for a
|
|
13
13
|
* listing abroad and whenever the index cannot be read.
|
|
@@ -96,26 +96,35 @@ export function indexedHolderPage(holdings: TickerHoldings): IndexedHolderList {
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
/** Rows the service gives a page, as `fn 13F` asks for them. */
|
|
100
|
-
const INDEX_PAGE_ROWS = 25;
|
|
101
99
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
100
|
+
* Rows the pane asks a page for. The service reads a stored quarter's holders
|
|
101
|
+
* by key and gives up to 200 a page; one that builds the page from the funds'
|
|
102
|
+
* filings (an older one, or a quarter not stored yet) gives 25, and the next
|
|
103
|
+
* page starts where it stopped.
|
|
104
104
|
*/
|
|
105
|
-
const
|
|
105
|
+
const INDEX_PAGE_ROWS = 100;
|
|
106
|
+
/** Rows the service gives a page at most, as one that builds it from the funds' filings does. */
|
|
107
|
+
const INDEX_PAGE_ROWS_MIN = 25;
|
|
108
|
+
/**
|
|
109
|
+
* Pages one list reads at most: twice what the most rows take at the
|
|
110
|
+
* smallest page, for pages whose options and exits leave fewer holders, and
|
|
111
|
+
* never without end.
|
|
112
|
+
*/
|
|
113
|
+
const INDEX_PAGES_MAX = Math.ceil(HOLDER_ROWS_MAX / INDEX_PAGE_ROWS_MIN) * 2;
|
|
106
114
|
|
|
107
115
|
/** The client the pages come through; headless reports pass their own. */
|
|
108
116
|
export type IndexedHolderClient = Pick<typeof apiClient, "getCloudSec13F">;
|
|
109
117
|
|
|
110
|
-
/** One page of
|
|
118
|
+
/** One page of the holders from `offset`, `rows` asked (the service may give fewer). */
|
|
111
119
|
export async function loadIndexedHolderPage(
|
|
112
120
|
ticker: string,
|
|
113
121
|
offset: number,
|
|
114
122
|
signal?: AbortSignal,
|
|
115
123
|
client: IndexedHolderClient = apiClient,
|
|
124
|
+
rows = INDEX_PAGE_ROWS,
|
|
116
125
|
): Promise<IndexedHolderList> {
|
|
117
126
|
signal?.throwIfAborted();
|
|
118
|
-
const holdings = await client.getCloudSec13F("ticker-holdings", { tickers: ticker, offset, limit:
|
|
127
|
+
const holdings = await client.getCloudSec13F("ticker-holdings", { tickers: ticker, offset, limit: rows }, { signal }) as TickerHoldings;
|
|
119
128
|
signal?.throwIfAborted();
|
|
120
129
|
if (!Array.isArray(holdings?.rows) || typeof holdings.period !== "string") throw new Error(`13F holders of ${ticker} are unreadable`);
|
|
121
130
|
const page = indexedHolderPage(holdings);
|
|
@@ -132,8 +141,9 @@ function appendIndexedHolders(current: IndexedHolderList, page: IndexedHolderLis
|
|
|
132
141
|
|
|
133
142
|
/**
|
|
134
143
|
* The largest `limit` holders, paging as far as that takes, never more than
|
|
135
|
-
* `INDEX_PAGES_MAX` pages
|
|
136
|
-
* loaded, marked partial; only a first
|
|
144
|
+
* `INDEX_PAGES_MAX` pages: one request for a stored quarter. A later page
|
|
145
|
+
* that fails keeps the holders already loaded, marked partial; only a first
|
|
146
|
+
* page that fails leaves the source's list.
|
|
137
147
|
*/
|
|
138
148
|
export async function loadIndexedHolders(
|
|
139
149
|
ticker: string,
|
|
@@ -141,12 +151,14 @@ export async function loadIndexedHolders(
|
|
|
141
151
|
signal?: AbortSignal,
|
|
142
152
|
client: IndexedHolderClient = apiClient,
|
|
143
153
|
): Promise<IndexedHolderList> {
|
|
144
|
-
|
|
154
|
+
// Asked for in full: exits and options can leave a page with fewer holders than rows.
|
|
155
|
+
const rows = Math.min(HOLDER_ROWS_MAX, Math.max(limit, 1));
|
|
156
|
+
let list = await loadIndexedHolderPage(ticker, 0, signal, client, rows);
|
|
145
157
|
for (let pages = 1; list.hasMore && list.rows.length < limit && pages < INDEX_PAGES_MAX; pages += 1) {
|
|
146
158
|
const offset = list.nextOffset;
|
|
147
159
|
let page: IndexedHolderList;
|
|
148
160
|
try {
|
|
149
|
-
page = await loadIndexedHolderPage(ticker, offset, signal, client);
|
|
161
|
+
page = await loadIndexedHolderPage(ticker, offset, signal, client, rows);
|
|
150
162
|
} catch (error) {
|
|
151
163
|
if (signal?.aborted) throw error;
|
|
152
164
|
logFailure("holders.index", "Read a later page of the 13F holder index failed", null)(error);
|
|
@@ -2,7 +2,6 @@ import type { PluginModule } from "../plugin-module";
|
|
|
2
2
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
3
3
|
import { beneficialOwnersCache } from "./beneficial-client";
|
|
4
4
|
import { holdersHeadless } from "./headless";
|
|
5
|
-
import { HoldersView } from "./pane";
|
|
6
5
|
|
|
7
6
|
export const holdersModule: PluginModule = {
|
|
8
7
|
setup(ctx) {
|
|
@@ -11,7 +10,7 @@ export const holdersModule: PluginModule = {
|
|
|
11
10
|
id: "holders",
|
|
12
11
|
name: "Holders",
|
|
13
12
|
order: 42,
|
|
14
|
-
component: HoldersView,
|
|
13
|
+
get component(): typeof import("./pane").HoldersView { const { HoldersView } = require("./pane"); return HoldersView; },
|
|
15
14
|
instruments: ["equity"],
|
|
16
15
|
});
|
|
17
16
|
},
|
|
@@ -24,7 +23,7 @@ export const holdersModule: PluginModule = {
|
|
|
24
23
|
id: "holders",
|
|
25
24
|
name: "Holders",
|
|
26
25
|
icon: "H",
|
|
27
|
-
component: HoldersView,
|
|
26
|
+
get component(): typeof import("./pane").HoldersView { const { HoldersView } = require("./pane"); return HoldersView; },
|
|
28
27
|
defaultPosition: "right",
|
|
29
28
|
tickerFollower: true,
|
|
30
29
|
defaultMode: "floating",
|
|
@@ -3,7 +3,6 @@ import { commandRequestParams } from "../shared/command-tab";
|
|
|
3
3
|
import { ipoCalendarCache } from "./client";
|
|
4
4
|
import { ipoCalendarHeadless } from "./headless";
|
|
5
5
|
import { IPO_CALENDAR_PANE_ID } from "./model";
|
|
6
|
-
import { IpoCalendarPane } from "./pane";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* `IPO japan` opens the calendar on that search, and so does a later `IPO europe`: one pane, the open one
|
|
@@ -44,7 +43,7 @@ export const ipoCalendarPlugin: GloomPlugin = {
|
|
|
44
43
|
id: IPO_CALENDAR_PANE_ID,
|
|
45
44
|
name: "IPO Calendar",
|
|
46
45
|
icon: "I",
|
|
47
|
-
component: IpoCalendarPane,
|
|
46
|
+
get component(): typeof import("./pane").IpoCalendarPane { const { IpoCalendarPane } = require("./pane"); return IpoCalendarPane; },
|
|
48
47
|
defaultPosition: "right",
|
|
49
48
|
defaultMode: "floating",
|
|
50
49
|
defaultFloatingSize: { width: 110, height: 28 },
|
|
@@ -3,14 +3,13 @@ import type { PluginModule } from "../plugin-module";
|
|
|
3
3
|
import { IV_SERIES } from "./series";
|
|
4
4
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
5
5
|
import { ivHistoryHeadless, ivScreenHeadless } from "./headless";
|
|
6
|
-
import { IvHistoryPane } from "./pane";
|
|
7
6
|
import { IvScreenPane, VCA_SCOPE_OPTIONS } from "./screen-pane";
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
export const ivHistoryModule: PluginModule = {
|
|
11
10
|
series: IV_SERIES,
|
|
12
11
|
panes: [{
|
|
13
|
-
id: "iv-history", name: "Implied Volatility History", icon: "V", component: IvHistoryPane,
|
|
12
|
+
id: "iv-history", name: "Implied Volatility History", icon: "V", get component(): typeof import("./pane").IvHistoryPane { const { IvHistoryPane } = require("./pane"); return IvHistoryPane; },
|
|
14
13
|
defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 118, height: 36 },
|
|
15
14
|
tableExport: true,
|
|
16
15
|
settings: { title: "Implied Volatility History", fields: [
|