@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
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
type QueryBarFilter,
|
|
33
33
|
type DataTableVisibleRange,
|
|
34
34
|
} from "../../../components";
|
|
35
|
+
import { statGridWithin } from "../../../components/ui/stat-grid";
|
|
35
36
|
import { useShortcut } from "../../../react/input";
|
|
36
37
|
import { useOptionalDialog, type AlertContext } from "../../../ui/dialog";
|
|
37
38
|
import { useLiveQuoteEntries, useQuoteUpdates } from "../../../state/hooks/quote-streaming";
|
|
@@ -980,10 +981,10 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
980
981
|
const expiryInline = Math.floor((statWidth - 2 * (cellColumns - 1)) / cellColumns) >= expiryCells;
|
|
981
982
|
const statColumns = expiryInline ? cellColumns : figureColumns;
|
|
982
983
|
const statItems = [expiryInline ? expiryItem : { ...expiryItem, wide: true }, ...figures];
|
|
983
|
-
|
|
984
|
-
//
|
|
985
|
-
const
|
|
986
|
-
|
|
984
|
+
// A short band gives up figures from the end, by lines: a row that wraps
|
|
985
|
+
// takes two. The expiry leads, so it stays.
|
|
986
|
+
const summaryLines = Math.min(statGridRows(statItems, statWidth, statColumns), Math.max(0, height - 6));
|
|
987
|
+
const { items: shownStatItems, lines: summaryRowCount } = statGridWithin(statItems, statWidth, summaryLines, statColumns);
|
|
987
988
|
// No term here follows the selection or the load, so an empty cold-expiry
|
|
988
989
|
// response cannot resize the table: growing it during loading would turn a
|
|
989
990
|
// clamped scroll into apparent user navigation.
|
|
@@ -997,7 +998,7 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
997
998
|
<QueryBar width={Math.max(1, width - 2)} filters={queryFilters} />
|
|
998
999
|
|
|
999
1000
|
{summaryRowCount > 0 && (
|
|
1000
|
-
<StatGrid items={shownStatItems} width={statWidth} columns={statColumns} />
|
|
1001
|
+
<StatGrid items={shownStatItems} width={statWidth} columns={statColumns} maxLines={summaryLines} />
|
|
1001
1002
|
)}
|
|
1002
1003
|
|
|
1003
1004
|
{isOpt && parsed && (
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
OPTIONS_CALCULATOR_PANE_ID,
|
|
4
4
|
OPTIONS_CALCULATOR_TEMPLATE_ID,
|
|
5
5
|
} from "./model";
|
|
6
|
-
import { OptionsCalculatorPane } from "./pane";
|
|
7
6
|
import { optionsCalculatorHeadless } from "./headless";
|
|
8
7
|
|
|
9
8
|
export const optionsCalculatorModule: PluginModule = {
|
|
@@ -12,7 +11,7 @@ export const optionsCalculatorModule: PluginModule = {
|
|
|
12
11
|
id: OPTIONS_CALCULATOR_PANE_ID,
|
|
13
12
|
name: "Options Calculator",
|
|
14
13
|
icon: "V",
|
|
15
|
-
component: OptionsCalculatorPane,
|
|
14
|
+
get component(): typeof import("./pane").OptionsCalculatorPane { const { OptionsCalculatorPane } = require("./pane"); return OptionsCalculatorPane; },
|
|
16
15
|
defaultPosition: "right",
|
|
17
16
|
defaultMode: "floating",
|
|
18
17
|
defaultFloatingSize: { width: 96, height: 28 },
|
|
@@ -3,7 +3,6 @@ import type { PluginModule } from "../plugin-module";
|
|
|
3
3
|
import { createTickerSurfacePaneTemplate, type TickerSurfacePaneTemplateOptions } from "../shared/ticker-surface";
|
|
4
4
|
import { optionsPositioningHeadless } from "./headless";
|
|
5
5
|
import { isPositioningTab, type PositioningTab } from "./model";
|
|
6
|
-
import { OptionsPositioningPane } from "./pane";
|
|
7
6
|
|
|
8
7
|
/** The underlying OPX opens on when no ticker is active. */
|
|
9
8
|
const DEFAULT_UNDERLYING = "SPY";
|
|
@@ -45,7 +44,7 @@ export const optionsPositioningModule: PluginModule = {
|
|
|
45
44
|
id: "options-positioning",
|
|
46
45
|
name: "Options Positioning",
|
|
47
46
|
icon: "O",
|
|
48
|
-
component: OptionsPositioningPane,
|
|
47
|
+
get component(): typeof import("./pane").OptionsPositioningPane { const { OptionsPositioningPane } = require("./pane"); return OptionsPositioningPane; },
|
|
49
48
|
defaultPosition: "right",
|
|
50
49
|
tickerFollower: true,
|
|
51
50
|
defaultMode: "floating",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
3
|
-
import { OptionsScenarioPane } from "./pane";
|
|
4
3
|
import { optionsScenarioHeadless } from "./headless";
|
|
5
|
-
import { CspScreenPane } from "./csp-screen-pane";
|
|
6
4
|
import { cspScreenHeadless } from "./csp-screen-headless";
|
|
7
5
|
|
|
8
6
|
const scenarioSettingKeys = ["legs", "strategy", "strike", "delta", "putStrike", "callStrike", "shares", "expiration", "spot", "rate", "dividendYield", "currency", "asOf", "date", "volShift",
|
|
@@ -10,7 +8,7 @@ const scenarioSettingKeys = ["legs", "strategy", "strike", "delta", "putStrike",
|
|
|
10
8
|
|
|
11
9
|
export const optionsScenarioModule: PluginModule = {
|
|
12
10
|
panes: [{
|
|
13
|
-
id: "options-scenario", name: "Options Scenario", icon: "V", component: OptionsScenarioPane,
|
|
11
|
+
id: "options-scenario", name: "Options Scenario", icon: "V", get component(): typeof import("./pane").OptionsScenarioPane { const { OptionsScenarioPane } = require("./pane"); return OptionsScenarioPane; },
|
|
14
12
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 126, height: 36 },
|
|
15
13
|
tableExport: true, headless: optionsScenarioHeadless,
|
|
16
14
|
settings: {
|
|
@@ -26,7 +24,7 @@ export const optionsScenarioModule: PluginModule = {
|
|
|
26
24
|
],
|
|
27
25
|
},
|
|
28
26
|
}, {
|
|
29
|
-
id: "csp-screen", name: "Options Strategy Screen", icon: "V", component: CspScreenPane,
|
|
27
|
+
id: "csp-screen", name: "Options Strategy Screen", icon: "V", get component(): typeof import("./csp-screen-pane").CspScreenPane { const { CspScreenPane } = require("./csp-screen-pane"); return CspScreenPane; },
|
|
30
28
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 144, height: 24 },
|
|
31
29
|
tableExport: true, headless: cspScreenHeadless,
|
|
32
30
|
settings: {
|
|
@@ -4,7 +4,6 @@ import { commandRequestParams } from "../shared/command-tab";
|
|
|
4
4
|
import { PERP_SERIES } from "./series";
|
|
5
5
|
import { perpsCache, perpsDepthCache, perpsHistoryCache, perpsLiquidationsCache, perpsListCache, perpsMarketCache, perpsRankingsCache } from "./client";
|
|
6
6
|
import { perpsHeadless } from "./headless";
|
|
7
|
-
import { PerpsPane } from "./pane";
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* A named market has its own pane, and asking for it again (PERP, a stock's perpetual row in DES or QQ) brings that
|
|
@@ -22,7 +21,7 @@ const PERP_CACHES = [perpsCache, perpsHistoryCache, perpsListCache, perpsMarketC
|
|
|
22
21
|
|
|
23
22
|
export const perpsModule: PluginModule = {
|
|
24
23
|
series: PERP_SERIES,
|
|
25
|
-
panes: [{ id: "perps", name: "Perpetuals", icon: "P", component: PerpsPane, defaultPosition: "right", defaultMode: "floating",
|
|
24
|
+
panes: [{ id: "perps", name: "Perpetuals", icon: "P", get component(): typeof import("./pane").PerpsPane { const { PerpsPane } = require("./pane"); return PerpsPane; }, defaultPosition: "right", defaultMode: "floating",
|
|
26
25
|
defaultFloatingSize: { width: 138, height: 34 }, tableExport: true, headless: perpsHeadless }],
|
|
27
26
|
paneTemplates: [{ id: "perps-pane", paneId: "perps", label: "Perpetuals", description: "Perpetual funding, open interest and premiums by market and venue, with rankings, a venue comparison, liquidations, order book depth and Pro history.",
|
|
28
27
|
keywords: ["perp", "perpetual", "perpetuals", "funding", "funding rate", "open interest", "hip-3", "stock perps", "basis", "basis trade", "carry", "venue", "funding spread", "long short ratio", "long/short", "positioning", "liquidations", "liquidation", "order book depth", "market depth", "liquidity"],
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { PLUGIN_AUTO_UPDATE_SETTING } from "../../auto-update";
|
|
2
2
|
import type { PluginModule } from "../plugin-module";
|
|
3
3
|
import { PLUGIN_MARKETPLACE_PANE_ID } from "../../marketplace/ids";
|
|
4
|
-
import { PluginMarketplacePane } from "./pane";
|
|
5
4
|
|
|
6
5
|
export const pluginMarketplaceModule: PluginModule = {
|
|
7
6
|
panes: [
|
|
@@ -9,8 +8,8 @@ export const pluginMarketplaceModule: PluginModule = {
|
|
|
9
8
|
id: PLUGIN_MARKETPLACE_PANE_ID,
|
|
10
9
|
name: "Plugins",
|
|
11
10
|
icon: "P",
|
|
12
|
-
component: PluginMarketplacePane,
|
|
13
|
-
reportFreshness: { status: "not-a-feed", basis: "plugin listings" },
|
|
11
|
+
get component(): typeof import("./pane").PluginMarketplacePane { const { PluginMarketplacePane } = require("./pane"); return PluginMarketplacePane; },
|
|
12
|
+
reportFreshness: { status: "not-a-feed", basis: "plugin listings", local: true },
|
|
14
13
|
defaultPosition: "right",
|
|
15
14
|
defaultMode: "floating",
|
|
16
15
|
defaultFloatingSize: { width: 118, height: 34 },
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type DataTableKeyEvent,
|
|
22
22
|
type PaneHint,
|
|
23
23
|
} from "../../../components";
|
|
24
|
+
import { ageFooterSegment } from "../../../components/layout/pane/footer";
|
|
24
25
|
import { loadingErrorFooterInfo } from "../../../components/data-table/table-pane";
|
|
25
26
|
import { useRowNotes } from "../../../components/ui/row-notes";
|
|
26
27
|
import { colors } from "../../../theme/colors";
|
|
@@ -922,7 +923,7 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
|
|
|
922
923
|
info.push({ id: "restart", parts: [{ text: "restart to finish loading", tone: "warning" }] });
|
|
923
924
|
}
|
|
924
925
|
if (status === "ready" && fetchedAt && !stale && !busy) {
|
|
925
|
-
info.push(
|
|
926
|
+
info.push(ageFooterSegment(formatRelativeAge(fetchedAt)));
|
|
926
927
|
}
|
|
927
928
|
|
|
928
929
|
const hints: PaneHint[] = [];
|
|
@@ -2,6 +2,7 @@ import { Fragment, createElement, type ReactNode } from "react";
|
|
|
2
2
|
import type {
|
|
3
3
|
GloomPlugin,
|
|
4
4
|
GloomPluginContext,
|
|
5
|
+
PaneDef,
|
|
5
6
|
} from "../../types/plugin";
|
|
6
7
|
import { pluginContextInNamespace } from "../registry/plugin-state";
|
|
7
8
|
import { withPluginStateNamespace } from "../runtime/context";
|
|
@@ -63,6 +64,19 @@ function moduleEntry(input: PluginModuleInput): PluginModuleEntry {
|
|
|
63
64
|
return "module" in input ? input : { module: input };
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/** Keep a pane's component lazy while moving its state into another plugin. */
|
|
68
|
+
function paneInNamespace(pane: PaneDef, stateId: string): PaneDef {
|
|
69
|
+
let component: PaneDef["component"] | undefined;
|
|
70
|
+
return Object.create(Object.getPrototypeOf(pane), {
|
|
71
|
+
...Object.getOwnPropertyDescriptors(pane),
|
|
72
|
+
component: {
|
|
73
|
+
enumerable: true,
|
|
74
|
+
configurable: true,
|
|
75
|
+
get: () => component ??= withPluginStateNamespace(stateId, pane.component),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
66
80
|
/**
|
|
67
81
|
* The module as it runs under a namespace other than its plugin's: every
|
|
68
82
|
* surface it renders and the context its setup receives are moved there,
|
|
@@ -76,7 +90,7 @@ function moduleInNamespace(module: PluginModule, stateId: string): PluginModule
|
|
|
76
90
|
return {
|
|
77
91
|
...module,
|
|
78
92
|
...(module.panes ? {
|
|
79
|
-
panes: module.panes.map((pane) => (
|
|
93
|
+
panes: module.panes.map((pane) => paneInNamespace(pane, stateId)),
|
|
80
94
|
} : {}),
|
|
81
95
|
...(slots ? { slots } : {}),
|
|
82
96
|
...(module.setup ? {
|
|
@@ -84,7 +98,7 @@ function moduleInNamespace(module: PluginModule, stateId: string): PluginModule
|
|
|
84
98
|
const scoped = pluginContextInNamespace(ctx, stateId);
|
|
85
99
|
return module.setup!({
|
|
86
100
|
...scoped,
|
|
87
|
-
registerPane: (pane) => ctx.registerPane(
|
|
101
|
+
registerPane: (pane) => ctx.registerPane(paneInNamespace(pane, stateId)),
|
|
88
102
|
registerTickerResearchTab: (tab) => ctx.registerTickerResearchTab({
|
|
89
103
|
...tab,
|
|
90
104
|
component: withPluginStateNamespace(stateId, tab.component),
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { logFailure } from "../../../../utils/log-failure";
|
|
2
|
-
import { createBaseConverter } from "../../../../cli/base-converter";
|
|
2
|
+
import { createBaseConverter, type BaseConversion } from "../../../../cli/base-converter";
|
|
3
|
+
import { quotedFxPair } from "../../../../utils/currency-pair";
|
|
4
|
+
import { formatFxRate } from "../../../../cli/fx-pair";
|
|
3
5
|
import { savedListingName } from "../../../../cli/listing-arg";
|
|
4
6
|
import { currentListingName } from "../../../../tickers/listing-name";
|
|
5
7
|
import { withMarketData } from "../../../../cli/scoped-context";
|
|
@@ -575,6 +577,38 @@ export function weightBasisLine(allocation: Pick<PortfolioAllocation, "weightBas
|
|
|
575
577
|
return weightBasisNote(allocation, (value) => formatAllocationMoney(value, currency, { whole: true }));
|
|
576
578
|
}
|
|
577
579
|
|
|
580
|
+
/** `47h old`, or `3 days old` past two days: how far behind a stale rate is. */
|
|
581
|
+
function rateAge(asOf: number, now: number): string {
|
|
582
|
+
const hours = Math.max(0, Math.floor((now - asOf) / 3_600_000));
|
|
583
|
+
return hours < 48 ? `${hours}h old` : `${Math.floor(hours / 24)} days old`;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/** A rate the report converted at, as markets quote the pair and `fx` writes it, for `--json`. */
|
|
587
|
+
function fxRateRecord({ currency, baseCurrency, rate, asOf, stale }: BaseConversion) {
|
|
588
|
+
const quoted = quotedFxPair(currency, baseCurrency, rate);
|
|
589
|
+
return { currency, baseCurrency, pair: quoted.pair, rate: Number(formatFxRate(quoted.rate)), asOf: asOf == null ? null : new Date(asOf).toISOString(), stale };
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* `FX: USD/ZAR 16.7237, as of Fri 30 Oct 18:12 UTC`: each rate a portfolio's
|
|
594
|
+
* holdings in another currency were converted at, as markets quote the pair,
|
|
595
|
+
* dated; one past its fresh window says it is stale and how old. Null when
|
|
596
|
+
* nothing was converted.
|
|
597
|
+
*/
|
|
598
|
+
function fxRatesLine(
|
|
599
|
+
conversions: readonly BaseConversion[],
|
|
600
|
+
{ now = Date.now(), warning = (text: string) => text }: { now?: number; warning?: (text: string) => string } = {},
|
|
601
|
+
): string | null {
|
|
602
|
+
const rates = conversions.filter((conversion) => conversion.currency !== conversion.baseCurrency && Number.isFinite(conversion.rate));
|
|
603
|
+
if (rates.length === 0) return null;
|
|
604
|
+
return `FX: ${rates.map((conversion) => {
|
|
605
|
+
const quoted = quotedFxPair(conversion.currency, conversion.baseCurrency, conversion.rate);
|
|
606
|
+
const dated = conversion.asOf == null ? "" : `, as of ${formatUtcDayTime({ time: conversion.asOf, dateOnly: false }, now)} UTC`;
|
|
607
|
+
const text = `${quoted.pair} ${formatFxRate(quoted.rate)}${dated}`;
|
|
608
|
+
return conversion.stale ? warning(`${text} (stale${conversion.asOf == null ? "" : `, ${rateAge(conversion.asOf, now)}`})`) : text;
|
|
609
|
+
}).join("; ")}`;
|
|
610
|
+
}
|
|
611
|
+
|
|
578
612
|
/** What the TOTAL row weighs: all of a total; nothing on a gross base, where the weights net to the book's net over its gross. */
|
|
579
613
|
function totalWeight(allocation: Pick<PortfolioAllocation, "weightBasis" | "total">): number | null {
|
|
580
614
|
return allocation.weightBasis === "total" && allocation.total != null && allocation.total > 0 ? 100 : null;
|
|
@@ -818,7 +852,12 @@ async function showCollectionWithMarketData(
|
|
|
818
852
|
})), dividends, toBase);
|
|
819
853
|
const lotIncome = income.lots;
|
|
820
854
|
const totalIncome = income.totalIncome;
|
|
821
|
-
|
|
855
|
+
// The yield is on what is invested: cash pays no dividend, so it does not dilute it. A long-only book's is
|
|
856
|
+
// its priced holdings' net value. A book that holds a short is weighed on its gross exposure, and so is its
|
|
857
|
+
// yield: the net shrinks as the hedge grows (long 100k against short 80k nets 20k), which would inflate it.
|
|
858
|
+
const hedged = allocation.weightBasis === "gross";
|
|
859
|
+
const yieldBase = hedged ? allocation.weightBase : exposure.net;
|
|
860
|
+
const portfolioYield = totalIncome != null && yieldBase != null && yieldBase > 0 ? (totalIncome / yieldBase) * 100 : null;
|
|
822
861
|
const incomeUnavailable = [...income.gaps].map(([symbol, gap]) => ({ symbol, ...gap }));
|
|
823
862
|
const showTargets = allocation.targetSum != null;
|
|
824
863
|
// Never dropped on a narrow terminal, so --explain reads the terms off them.
|
|
@@ -956,7 +995,9 @@ async function showCollectionWithMarketData(
|
|
|
956
995
|
totalUnrealizedPnl: totalPnlValue,
|
|
957
996
|
annualIncome: totalIncome, knownAnnualIncome: income.knownIncome, portfolioYield,
|
|
958
997
|
incomeComplete: income.gaps.size === 0, incomeUnavailable,
|
|
959
|
-
incomeBasis: "Trailing twelve-month dividends per share times shares held, in the portfolio's currency; a short owes the dividend, so its income is negative. Portfolio yield is the total over the
|
|
998
|
+
incomeBasis: "Trailing twelve-month dividends per share times shares held, in the portfolio's currency; a short owes the dividend, so its income is negative. Portfolio yield is the total over the invested value without cash: the priced holdings' net value (exposure.net), or for a book that holds a short its gross exposure (weightBase), as its weights are; annualIncome is null when any holding's income is unknown, and knownAnnualIncome sums the holdings that have one.",
|
|
999
|
+
// Each rate a holding in another currency was converted at, dated, and whether it is stale.
|
|
1000
|
+
fxRates: toBase.conversions().filter((conversion) => conversion.currency !== conversion.baseCurrency).map(fxRateRecord),
|
|
960
1001
|
complete: unavailablePnl.size === 0 && unavailableCost.size === 0 && unavailableMarketValue.size === 0 && income.gaps.size === 0,
|
|
961
1002
|
unavailableSymbols: [...new Set([...unavailablePnl, ...unavailableCost, ...unavailableMarketValue])],
|
|
962
1003
|
unavailableCostSymbols: [...unavailableCost], brokerPnlSymbols: [...brokerPnlSymbols],
|
|
@@ -1098,7 +1139,16 @@ async function showCollectionWithMarketData(
|
|
|
1098
1139
|
console.log("");
|
|
1099
1140
|
if (basis === "regular-close") console.log(dayMoveLine(dayMove, currency, colorBySign, cliStyles.muted));
|
|
1100
1141
|
if (valued.some((entry) => entry.position)) console.log(exposureLine(exposure, currency));
|
|
1142
|
+
// The rates the holdings in another currency are valued at, dated, so a stale one is seen beside the value it moves.
|
|
1143
|
+
const fxLine = fxRatesLine(toBase.conversions(), { warning: cliStyles.warning });
|
|
1144
|
+
if (fxLine) console.log(fxLine);
|
|
1101
1145
|
const notes: string[] = [];
|
|
1146
|
+
// TOTAL's yield says what it divides: a hedged book's gross exposure, or, with cash in the book, the holdings alone.
|
|
1147
|
+
if (showIncome && portfolioYield != null && yieldBase != null && hedged) {
|
|
1148
|
+
notes.push(cliStyles.muted(`TOTAL yield is income over the gross exposure of ${wholeMoney(yieldBase)}, as the book holds a short; cash excluded.`));
|
|
1149
|
+
} else if (showIncome && portfolioYield != null && yieldBase != null && cashExport?.value) {
|
|
1150
|
+
notes.push(cliStyles.muted(`TOTAL yield is income over the invested ${wholeMoney(yieldBase)}, cash excluded.`));
|
|
1151
|
+
}
|
|
1102
1152
|
const weightNote = weightBasisLine(allocation, currency);
|
|
1103
1153
|
if (weightNote) notes.push(cliStyles.muted(weightNote));
|
|
1104
1154
|
// The notes name the rows as the table does.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { PaneTemplateContext, PaneTemplateInstanceConfig, ShortcutArgError } from "../../../types/plugin";
|
|
2
2
|
import type { PluginModule } from "../plugin-module";
|
|
3
3
|
import { unknownCollectionArgError } from "../../../portfolio/collection-name";
|
|
4
|
-
import { PortfolioListPane } from "./pane";
|
|
5
4
|
import {
|
|
6
5
|
buildPortfolioPaneSettingsDef,
|
|
7
6
|
getPortfolioPaneSettings,
|
|
@@ -54,7 +53,7 @@ export const portfolioListModule: PluginModule = {
|
|
|
54
53
|
reportFreshness: { source: "Local portfolio and Gloom Cloud" },
|
|
55
54
|
name: "Portfolio",
|
|
56
55
|
icon: "P",
|
|
57
|
-
component: PortfolioListPane,
|
|
56
|
+
get component(): typeof import("./pane").PortfolioListPane { const { PortfolioListPane } = require("./pane"); return PortfolioListPane; },
|
|
58
57
|
defaultPosition: "left",
|
|
59
58
|
defaultMode: "floating",
|
|
60
59
|
defaultWidth: "40%",
|
|
@@ -34,6 +34,7 @@ import { convertCurrency } from "../../../../utils/format";
|
|
|
34
34
|
import { nextHeaderSort } from "../../../../utils/sort-values";
|
|
35
35
|
import { getSharedMarketDataCoordinator } from "../../../../market-data/coordinator";
|
|
36
36
|
import type { TickerRecord } from "../../../../types/ticker";
|
|
37
|
+
import type { TickerFinancials } from "../../../../types/financials";
|
|
37
38
|
import type { PaneProps } from "../../../../types/plugin";
|
|
38
39
|
import type { InstrumentRef } from "../../../../market-data/request-types";
|
|
39
40
|
import {
|
|
@@ -56,6 +57,7 @@ import {
|
|
|
56
57
|
} from "../summary";
|
|
57
58
|
import { usePortfolioAccountState } from "../summary/live-accounts";
|
|
58
59
|
import { useOfflineBrokerFooter } from "../../shared/broker-reconnect";
|
|
60
|
+
import { useQuoteDelayFooter } from "../../shared/cloud-upgrade";
|
|
59
61
|
import {
|
|
60
62
|
getCollectionEntries,
|
|
61
63
|
getPortfolioPaneSettings,
|
|
@@ -598,6 +600,8 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
598
600
|
summaryAccountState,
|
|
599
601
|
], FOOTER_TOTALS_THROTTLE_MS);
|
|
600
602
|
const fxWarning = !!(fxStatus.stale || fxStatus.unknownTime || fxStatus.unavailable);
|
|
603
|
+
// Signed out or free, the rows' prices are the delayed ones; a list of coins is live and says nothing.
|
|
604
|
+
const quoteDelay = useQuoteDelayFooter(rowQuotes(sortedTickers, financialsMap), "portfolio-delay");
|
|
601
605
|
const summaryNotices = isPortfolioTab
|
|
602
606
|
? buildPortfolioSummaryNotices({
|
|
603
607
|
totals: portfolioSummaryTotals,
|
|
@@ -675,6 +679,7 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
675
679
|
...offlineBrokerFooter.status,
|
|
676
680
|
...(fxStatusText && !fxWarning ? [{ id: "fx", parts: [{ text: `FX ${fxStatusText}`, tone: "muted" as const }] }] : []),
|
|
677
681
|
...(showsPerpetuals && perpetuals.stale ? [{ id: "perpetuals", parts: [{ text: "perpetuals stale", tone: "warning" as const }] }] : []),
|
|
682
|
+
...(quoteDelay ? [quoteDelay] : []),
|
|
678
683
|
],
|
|
679
684
|
hints: [
|
|
680
685
|
...offlineBrokerFooter.hints,
|
|
@@ -714,6 +719,7 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
714
719
|
isPortfolioTab,
|
|
715
720
|
offlineBrokerFooter,
|
|
716
721
|
perpetuals.stale,
|
|
722
|
+
quoteDelay,
|
|
717
723
|
removalCollection,
|
|
718
724
|
removeSelectedTicker,
|
|
719
725
|
selectedTicker,
|
|
@@ -840,3 +846,8 @@ export function PortfolioListPane({ focused, width, height }: PaneProps) {
|
|
|
840
846
|
</Box>
|
|
841
847
|
);
|
|
842
848
|
}
|
|
849
|
+
|
|
850
|
+
/** The quotes of the rows, in order, for the delayed clock, which stops at the first late one. */
|
|
851
|
+
function* rowQuotes(tickers: readonly TickerRecord[], financialsMap: ReadonlyMap<string, TickerFinancials>) {
|
|
852
|
+
for (const ticker of tickers) yield financialsMap.get(ticker.metadata.ticker)?.quote;
|
|
853
|
+
}
|
|
@@ -12,7 +12,7 @@ import { usePluginAppActions } from "../../../runtime";
|
|
|
12
12
|
import { isDialogOpen } from "../../../../ui/dialog-stack";
|
|
13
13
|
import { colors, priceColor } from "../../../../theme/colors";
|
|
14
14
|
import { formatPercentRaw } from "../../../../utils/format";
|
|
15
|
-
import { formatMarketPrice } from "../../../../market-data/market/format";
|
|
15
|
+
import { formatMarketPrice, quoteFormatOptions } from "../../../../market-data/market/format";
|
|
16
16
|
import { listingChoiceQuery, upsertTickerFromSearchResult } from "../../../../tickers/search";
|
|
17
17
|
import type { TickerRecord } from "../../../../types/ticker";
|
|
18
18
|
import { addTickerToPortfolio, addTickerToWatchlist } from "../../../../portfolio/mutations";
|
|
@@ -79,7 +79,7 @@ function QuickAddPreview({
|
|
|
79
79
|
const assetCategory = validation.ticker?.metadata.assetCategory
|
|
80
80
|
?? (validation.resolved.kind === "provider" ? validation.resolved.result.type : undefined);
|
|
81
81
|
const priceText = quote?.price != null
|
|
82
|
-
? formatMarketPrice(quote.price, { assetCategory, maxWidth: 12 })
|
|
82
|
+
? formatMarketPrice(quote.price, { ...quoteFormatOptions(quote, assetCategory), maxWidth: 12 })
|
|
83
83
|
: "-";
|
|
84
84
|
const changeValue = quote?.changePercent;
|
|
85
85
|
const name = tickerNameFromValidation(validation);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
|
+
import { sourceDidNotAnswer } from "../../../api-client/errors";
|
|
2
3
|
import type { GasStorageBoard, GasStorageHistory, GasStorageRow, GasStorageSeasonal } from "../../../api-client/europe-energy";
|
|
3
4
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
4
5
|
import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
@@ -77,7 +78,7 @@ export async function fetchGasStorage(client: Pick<GasApi, "getCloudEuropeGas">
|
|
|
77
78
|
} catch (error) {
|
|
78
79
|
throw unavailableOnServer(error, GAS_STORAGE_NOT_AVAILABLE, [404, 503]);
|
|
79
80
|
}
|
|
80
|
-
if (board.status === "unavailable") throw
|
|
81
|
+
if (board.status === "unavailable") throw sourceDidNotAnswer(GAS_STORAGE_NOT_AVAILABLE);
|
|
81
82
|
return board;
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -2,7 +2,6 @@ import type { PaneTemplateInstanceConfig } from "../../../types/plugin";
|
|
|
2
2
|
import type { PluginModule } from "../plugin-module";
|
|
3
3
|
import { powerBoardCache, powerDetailCache, powerHistoryCache } from "./client";
|
|
4
4
|
import { powerHeadless } from "./headless";
|
|
5
|
-
import { PowerPane } from "./pane";
|
|
6
5
|
import { carbonHistoryCache, carbonPriceCache, powerPriceHistoryCache, powerPricesCache } from "./prices-client";
|
|
7
6
|
import { powerPricesHeadless } from "./prices-headless";
|
|
8
7
|
import { CARBON_SELECTION, CARBON_SERIES, DAY_AHEAD_SERIES, isCarbonQuery, POWER_PRICE_MARKETS, powerPriceMarket } from "./prices-model";
|
|
@@ -15,7 +14,7 @@ const CACHES = [powerBoardCache, powerHistoryCache, powerDetailCache, powerPrice
|
|
|
15
14
|
export const powerModule: PluginModule = {
|
|
16
15
|
series: [...DAY_AHEAD_SERIES, ...CARBON_SERIES, ...GAS_STORAGE_SERIES],
|
|
17
16
|
// POWER sets the symbol param, EPWR the prices tab.
|
|
18
|
-
panes: [{ id: "power", name: "Power & Grid", icon: "P", component: PowerPane, defaultPosition: "right", defaultMode: "floating", identitySettings: ["tab", "symbol"],
|
|
17
|
+
panes: [{ id: "power", name: "Power & Grid", icon: "P", get component(): typeof import("./pane").PowerPane { const { PowerPane } = require("./pane"); return PowerPane; }, defaultPosition: "right", defaultMode: "floating", identitySettings: ["tab", "symbol"],
|
|
19
18
|
defaultFloatingSize: { width: 132, height: 34 }, tableExport: true, headless: powerHeadless }],
|
|
20
19
|
paneTemplates: [{ id: "power-pane", paneId: "power", label: "Power & Grid",
|
|
21
20
|
description: "Pro: interconnection queues, historical MW and outcomes, datacenter loads, utility exposure and generation capacity, with primary evidence.",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
|
+
import { sourceDidNotAnswer } from "../../../api-client/errors";
|
|
2
3
|
import type { CarbonBoard, CarbonHistory, EuropePowerBoard, EuropePowerHistory, PowerPriceRow } from "../../../api-client/europe-energy";
|
|
3
4
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
4
5
|
import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
@@ -87,7 +88,7 @@ export async function fetchPowerPrices(client: Pick<PricesApi, "getCloudEuropePo
|
|
|
87
88
|
} catch (error) {
|
|
88
89
|
throw unavailableOnServer(error, POWER_PRICES_NOT_AVAILABLE, [404, 503]);
|
|
89
90
|
}
|
|
90
|
-
if (board.status === "unavailable") throw
|
|
91
|
+
if (board.status === "unavailable") throw sourceDidNotAnswer(POWER_PRICES_NOT_AVAILABLE);
|
|
91
92
|
return board;
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -110,7 +111,7 @@ export async function fetchCarbonPrice(client: Pick<PricesApi, "getCloudEuropeCa
|
|
|
110
111
|
} catch (error) {
|
|
111
112
|
throw unavailableOnServer(error, CARBON_PRICE_NOT_AVAILABLE, [404, 503]);
|
|
112
113
|
}
|
|
113
|
-
if (board.status === "unavailable") throw
|
|
114
|
+
if (board.status === "unavailable") throw sourceDidNotAnswer(CARBON_PRICE_NOT_AVAILABLE);
|
|
114
115
|
return board;
|
|
115
116
|
}
|
|
116
117
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { ratePathCache } from "./client";
|
|
3
3
|
import { ratePathHeadless } from "./headless";
|
|
4
|
-
import { RatePathPane } from "./pane";
|
|
5
4
|
|
|
6
5
|
export const ratePathModule: PluginModule = {
|
|
7
|
-
panes: [{ id: "rate-path", name: "US Rate Path", icon: "R", component: RatePathPane,
|
|
6
|
+
panes: [{ id: "rate-path", name: "US Rate Path", icon: "R", get component(): typeof import("./pane").RatePathPane { const { RatePathPane } = require("./pane"); return RatePathPane; },
|
|
8
7
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 86, height: 30 },
|
|
9
8
|
tableExport: true, headless: ratePathHeadless }],
|
|
10
9
|
paneTemplates: [{
|
|
@@ -55,6 +55,8 @@ export function realizedVolHeadless(initialView: "graph" | "cone"): HeadlessPane
|
|
|
55
55
|
const lastBar = model.asOf && Number.isFinite(model.asOf.getTime()) ? model.asOf.toISOString().slice(0, 10) : null;
|
|
56
56
|
return {
|
|
57
57
|
freshness: { asOf: lastBar },
|
|
58
|
+
// The session still trading is left out of every estimate, and said, as BT says it.
|
|
59
|
+
...(model.formingNote ? { warnings: [`${model.formingNote}.`] } : {}),
|
|
58
60
|
sections: [
|
|
59
61
|
{ title: "Volatility cone", columns: [
|
|
60
62
|
{ key: "window", header: "Bars" },
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
3
|
-
import { RealizedVolPane } from "./pane";
|
|
4
3
|
import { realizedVolHeadless } from "./headless";
|
|
5
4
|
import { ESTIMATOR_OPTIONS, WINDOW_OPTIONS } from "./settings";
|
|
6
5
|
|
|
7
6
|
export const realizedVolModule: PluginModule = {
|
|
8
7
|
panes: [{
|
|
9
|
-
id: "realized-vol", name: "Realized Volatility", icon: "V", component: RealizedVolPane,
|
|
8
|
+
id: "realized-vol", name: "Realized Volatility", icon: "V", get component(): typeof import("./pane").RealizedVolPane { const { RealizedVolPane } = require("./pane"); return RealizedVolPane; },
|
|
10
9
|
defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 112, height: 36 },
|
|
11
10
|
tableExport: true,
|
|
12
11
|
quickSettings: [{ type: "toggle", key: "showIv", icon: "zap", label: "Current ATM IV" }],
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type RealizedVolatilityEstimator, type RollingRealizedVolatilityPoint, type VolatilityConeStatistics,
|
|
6
6
|
} from "../shared/volatility";
|
|
7
7
|
import type { SurfaceExpiry, SurfaceSnapshot } from "../vol-surface/model";
|
|
8
|
+
import { formingBarsState, isFormingBar } from "../shared/forming-bars";
|
|
8
9
|
|
|
9
10
|
export interface RealizedVolatilityModel {
|
|
10
11
|
symbol: string;
|
|
@@ -30,6 +31,12 @@ export interface RealizedVolatilityModel {
|
|
|
30
31
|
* history covers the lookback and every window.
|
|
31
32
|
*/
|
|
32
33
|
historyNote: string | null;
|
|
34
|
+
/**
|
|
35
|
+
* The newest session left out while it is not final, as BT leaves it out:
|
|
36
|
+
* "2026-10-30 is still forming (partial) and left out: the estimates run
|
|
37
|
+
* through 2026-10-29, the last complete session". Null when every bar is final.
|
|
38
|
+
*/
|
|
39
|
+
formingNote: string | null;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
/** "the 180-bar window", "the 180- and 260-bar windows", "the 90-, 180- and 260-bar windows". */
|
|
@@ -77,8 +84,15 @@ export function projectRealizedVolatility(
|
|
|
77
84
|
}
|
|
78
85
|
dates.set(date.getTime(), { ...point, date });
|
|
79
86
|
}
|
|
80
|
-
const
|
|
87
|
+
const served = [...dates.entries()].sort(([a], [b]) => a - b).map(([, point]) => point);
|
|
88
|
+
// A session still trading moves every estimate it is in until it ends: left out, as BT leaves it out, and said.
|
|
89
|
+
const forming = served.filter(isFormingBar);
|
|
90
|
+
const history = forming.length ? served.filter((point) => !isFormingBar(point)) : served;
|
|
81
91
|
const asOf = history.at(-1)?.date ?? null;
|
|
92
|
+
const day = (date: Date) => date.toISOString().slice(0, 10);
|
|
93
|
+
const formingNote = forming.length === 0 ? null
|
|
94
|
+
: `${forming.map((point) => day(point.date)).join(", ")} ${formingBarsState(forming)} and left out`
|
|
95
|
+
+ `${asOf ? `: the estimates run through ${day(asOf)}, the last complete session` : ""}`;
|
|
82
96
|
const cutoff = asOf ? lookbackCutoff(asOf, lookbackYears) : 0;
|
|
83
97
|
const cadenceIssue = realizedVolatilityCadenceIssue(history);
|
|
84
98
|
if (cadenceIssue) warnings.push(cadenceIssue);
|
|
@@ -109,7 +123,7 @@ export function projectRealizedVolatility(
|
|
|
109
123
|
: "the long windows' cone covers part of the lookback"}`;
|
|
110
124
|
return { symbol: options.symbol.trim().toUpperCase(), estimator, lookbackYears, windows,
|
|
111
125
|
history: visible, series: visibleSeries, cone, asOf, barsPerYear: barsPerYear(history.map((point) => point.date)),
|
|
112
|
-
warnings: [...new Set(warnings)], shortWindows, historyNote };
|
|
126
|
+
warnings: [...new Set(warnings)], shortWindows, historyNote, formingNote };
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
interface CurrentAtmIvReference {
|
|
@@ -115,10 +115,10 @@ export function RealizedVolPane({ width, height, focused }: PaneProps) {
|
|
|
115
115
|
symbol: history.data.symbol, estimator, windows, lookbackYears: Number(lookback) === 2 ? 2 : 1,
|
|
116
116
|
}) : null, [history.data, estimator, windows, lookback]);
|
|
117
117
|
const chartInput = useMemo(() => model ? { history: model.history, rolling: model.series, windows: model.windows,
|
|
118
|
-
priceUnit: priceSeriesUnit(quote?.currency ?? "", quote?.instrumentType), iv: showIv ? iv.data?.reference ?? null : null } : null,
|
|
119
|
-
[model, quote?.currency, quote?.instrumentType, showIv, iv.data]);
|
|
118
|
+
priceUnit: priceSeriesUnit(quote?.currency ?? "", quote?.instrumentType, quote?.priceBasis), iv: showIv ? iv.data?.reference ?? null : null } : null,
|
|
119
|
+
[model, quote?.currency, quote?.instrumentType, quote?.priceBasis, showIv, iv.data]);
|
|
120
120
|
const notices = [identityError, history.error, history.data?.error, iv.error, ivRefresh.error, iv.data?.error,
|
|
121
|
-
...(model?.warnings ?? []), model?.historyNote, ...(showIv ? iv.data?.warnings ?? [] : []),
|
|
121
|
+
...(model?.warnings ?? []), model?.historyNote, model?.formingNote, ...(showIv ? iv.data?.warnings ?? [] : []),
|
|
122
122
|
...(showIv && symbol && !spotAvailable ? ["Current ATM IV unavailable: underlying quote is missing or stale"] : []),
|
|
123
123
|
].filter((value): value is string => !!value);
|
|
124
124
|
usePaneNoticeFooter({ registrationId: "realized-vol-notices", notices: [...new Set(notices)], focused });
|
|
@@ -2,7 +2,6 @@ import { isOnPortfolio } from "../../../shares/pane-image";
|
|
|
2
2
|
import type { PluginModule } from "../plugin-module";
|
|
3
3
|
import { rotationCache } from "./client";
|
|
4
4
|
import { rotationHeadless } from "./headless";
|
|
5
|
-
import { RelativeRotationPane } from "./pane";
|
|
6
5
|
import { DEFAULT_ROTATION_BENCHMARK } from "./model";
|
|
7
6
|
|
|
8
7
|
export const relativeRotationModule: PluginModule = {
|
|
@@ -11,7 +10,7 @@ export const relativeRotationModule: PluginModule = {
|
|
|
11
10
|
id: "relative-rotation",
|
|
12
11
|
name: "Relative Rotation",
|
|
13
12
|
icon: "R",
|
|
14
|
-
component: RelativeRotationPane,
|
|
13
|
+
get component(): typeof import("./pane").RelativeRotationPane { const { RelativeRotationPane } = require("./pane"); return RelativeRotationPane; },
|
|
15
14
|
defaultPosition: "right",
|
|
16
15
|
defaultMode: "floating",
|
|
17
16
|
defaultFloatingSize: { width: 100, height: 36 },
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
recommendationMix,
|
|
21
21
|
formatRatingLabel,
|
|
22
22
|
formatRatingTarget,
|
|
23
|
+
ratingReason,
|
|
23
24
|
recommendationTotal,
|
|
24
25
|
sortRatingRows,
|
|
25
26
|
type RatingColumnId,
|
|
@@ -43,6 +44,27 @@ const RATING_COLUMNS: HeadlessPaneColumn[] = [
|
|
|
43
44
|
{ key: "prior", header: "Prior" },
|
|
44
45
|
];
|
|
45
46
|
|
|
47
|
+
type RatingRow = AnalystResearchData["ratings"][number];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The reason coverage gave, on its own line under the action with its
|
|
51
|
+
* article; exports keep the line and the article's address as columns. Only
|
|
52
|
+
* a history with a reason somewhere has them.
|
|
53
|
+
*/
|
|
54
|
+
const REASON_COLUMNS: HeadlessPaneColumn[] = [
|
|
55
|
+
{
|
|
56
|
+
key: "reason",
|
|
57
|
+
header: "Reason",
|
|
58
|
+
underRow: true,
|
|
59
|
+
format: (_value, row) => ratingReason(row as unknown as RatingRow)?.text ?? "",
|
|
60
|
+
textFormat: (_value, row) => {
|
|
61
|
+
const reason = ratingReason(row as unknown as RatingRow);
|
|
62
|
+
return reason ? `${reason.text} · ${reason.url}` : "";
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{ key: "reasonUrl", header: "Reason URL", underRow: true, textFormat: () => "" },
|
|
66
|
+
];
|
|
67
|
+
|
|
46
68
|
const SORT_COLUMNS: Record<string, RatingColumnId> = {
|
|
47
69
|
date: "date",
|
|
48
70
|
firm: "firm",
|
|
@@ -170,14 +192,19 @@ export function createAnalystResearchHeadless(
|
|
|
170
192
|
direction: args.options.order === "asc" ? "asc" : "desc",
|
|
171
193
|
})
|
|
172
194
|
.slice(0, Number(args.options.limit))
|
|
173
|
-
.map((rating) =>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
195
|
+
.map((rating) => {
|
|
196
|
+
const reason = ratingReason(rating);
|
|
197
|
+
return {
|
|
198
|
+
...rating,
|
|
199
|
+
target: rating.currentPriceTarget ?? rating.priorPriceTarget ?? null,
|
|
200
|
+
currency: currency ?? null,
|
|
201
|
+
...(reason ? { reasonUrl: reason.url } : {}),
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
const columns = ratings.some((rating) => ratingReason(rating)) ? [...RATING_COLUMNS, ...REASON_COLUMNS] : RATING_COLUMNS;
|
|
178
205
|
const sections: HeadlessBundleSection[] = [
|
|
179
206
|
{ title: "Summary", entries: overviewEntries(data, quote) },
|
|
180
|
-
{ title: "Recent analyst actions", columns
|
|
207
|
+
{ title: "Recent analyst actions", columns, rows: ratings },
|
|
181
208
|
];
|
|
182
209
|
return {
|
|
183
210
|
sections,
|