@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
|
@@ -3,7 +3,10 @@ import type { CotBoardPayload, CotClass, CotClassSummary, CotContractPayload, Co
|
|
|
3
3
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
4
4
|
import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
5
5
|
import type { PricePoint } from "../../../types/financials";
|
|
6
|
-
import { COT_CLASSES, cotPriceMapping } from "./model";
|
|
6
|
+
import { COT_CLASSES, cotFrontPriceUnit, cotPriceMapping } from "./model";
|
|
7
|
+
import { futuresPriceUnit, loadFuturesContractTerms, loadFuturesPriceUnit } from "../../../market-data/futures-contract";
|
|
8
|
+
import { futuresBoardContract } from "../../../market-data/futures-board";
|
|
9
|
+
import { settleWithin } from "../../../utils/async-deadline";
|
|
7
10
|
|
|
8
11
|
export const cotBoardCache = createPluginCache<CotBoardPayload>({ kind: "cot-board", source: "gloom-cloud", schemaVersion: 1,
|
|
9
12
|
policy: { staleMs: 15 * 60_000, expireMs: 8 * 24 * 60 * 60_000 } });
|
|
@@ -88,7 +91,12 @@ export async function fetchCotContract(contractCode: string, family: CotFamily,
|
|
|
88
91
|
catch (error) { throw unavailable(error); }
|
|
89
92
|
}
|
|
90
93
|
export interface CotDetailData { payload: CotContractPayload; price: PricePoint[]; priceSymbol: string | null; priceAsOf: string | null; priceWarning: string | null }
|
|
91
|
-
|
|
94
|
+
/**
|
|
95
|
+
* A market's report and its front price. The price's unit is not waited for
|
|
96
|
+
* here: it joins the legend when it is known (loadCotPriceUnit).
|
|
97
|
+
*/
|
|
98
|
+
export async function loadCotDetail(contractCode: string, family: CotFamily,
|
|
99
|
+
client: Pick<typeof apiClient, "getCloudCotContract" | "getCloudHistory"> = apiClient,
|
|
92
100
|
positions: CotPositions = "futures-only"): Promise<CotDetailData> {
|
|
93
101
|
const payload = await fetchCotContract(contractCode, family, client, positions);
|
|
94
102
|
const mapping = cotPriceMapping(contractCode);
|
|
@@ -109,3 +117,21 @@ export async function loadCotDetail(contractCode: string, family: CotFamily, cli
|
|
|
109
117
|
priceWarning: result.stale ? "Front-price history is stale." : price.length ? null : "Front-price history unavailable." };
|
|
110
118
|
} catch { return { payload, price: [], priceSymbol: mapping.priceSymbol, priceAsOf: null, priceWarning: "Front-price history unavailable. Positioning remains available." }; }
|
|
111
119
|
}
|
|
120
|
+
|
|
121
|
+
/** How long the front price's legend waits for the contract terms before it writes the catalog's unit. */
|
|
122
|
+
const COT_PRICE_UNIT_WAIT_MS = 1_500;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The unit a market's front price is written in, as its legend writes it
|
|
126
|
+
* after the price (cotFrontPriceUnit): `USD/gal` for heating oil. It is the
|
|
127
|
+
* contract terms' unit, else the futures catalog's where the terms fail or
|
|
128
|
+
* have not answered within `waitMs`; null for a market with no front price.
|
|
129
|
+
* The chart draws without it, so a slow answer never holds the detail up.
|
|
130
|
+
*/
|
|
131
|
+
export async function loadCotPriceUnit(contractCode: string, client: Pick<typeof apiClient, "getCloudFuturesContract"> = apiClient,
|
|
132
|
+
waitMs = COT_PRICE_UNIT_WAIT_MS): Promise<string | null> {
|
|
133
|
+
const symbol = cotPriceMapping(contractCode)?.priceSymbol;
|
|
134
|
+
if (!symbol) return null;
|
|
135
|
+
const terms = await settleWithin(loadFuturesPriceUnit(symbol, (key) => loadFuturesContractTerms(key, client)), waitMs);
|
|
136
|
+
return cotFrontPriceUnit(contractCode, terms ?? futuresPriceUnit(futuresBoardContract(symbol), null));
|
|
137
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CotClass, CotClassSummary, CotContractPayload, CotHistoryPoint } from "../../../api-client/cot";
|
|
2
2
|
import type { HeadlessPaneColumn, HeadlessPaneDefinition } from "../../../types/plugin";
|
|
3
3
|
import { fetchCotBoard, fetchCotContract } from "./client";
|
|
4
|
-
import { COT_CLASSES, COT_FAMILIES, COT_MAJOR_CODES, COT_POSITIONS, COT_SCOPES, cotClass, cotContractCode, cotContractUnitText, cotFamily, cotInteger, cotMarketName, cotPositions, cotPositionsText, cotScope, cotUnknownMarketMessage } from "./model";
|
|
4
|
+
import { COT_CLASSES, COT_FAMILIES, COT_MAJOR_CODES, COT_POSITIONS, COT_SCOPES, cotClass, cotContractCode, cotContractUnitText, cotFamily, cotInteger, cotMarketName, cotPositions, cotPositionsText, cotProxyFor, cotProxyNote, cotScope, cotUnknownMarketMessage } from "./model";
|
|
5
5
|
import { COMMODITIES_VIEW_STATE } from "../commodities-energy/view-state";
|
|
6
6
|
import { REPORT_ROWS } from "../shared/headless-options";
|
|
7
7
|
|
|
@@ -67,6 +67,7 @@ export const cotHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
67
67
|
if (args.argument) {
|
|
68
68
|
const code = cotContractCode(args.argument);
|
|
69
69
|
if (!code) throw new Error(cotUnknownMarketMessage(args.argument));
|
|
70
|
+
const proxy = cotProxyFor(args.argument);
|
|
70
71
|
const data = await fetchCotContract(code, family, ctx.apiClient, positions);
|
|
71
72
|
const classes = data.positions.map((row) => row.id);
|
|
72
73
|
const limit = args.options.limit == null ? null : Number(args.options.limit);
|
|
@@ -80,6 +81,8 @@ export const cotHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
80
81
|
...classes.map((id) => ({ key: `${camelClass(id)}Net`, header: `${data.positions.find((row) => row.id === id)?.label ?? id} net`, align: "right" as const, format: signed }))],
|
|
81
82
|
rows: weeks.map((row) => ({ reportDate: row.reportDate, openInterest: row.openInterest,
|
|
82
83
|
...Object.fromEntries(row.positions.map((position) => [`${camelClass(position.id)}Net`, position.net])) })) }],
|
|
84
|
+
// "jet fuel" reads heating oil's positions, and says so.
|
|
85
|
+
...(proxy ? { notes: [cotProxyNote(proxy)] } : {}),
|
|
83
86
|
errors: data.gaps, metadata: { ...data, complete: data.status === "available" } };
|
|
84
87
|
}
|
|
85
88
|
const traderClass = cotClass(family, args.options.traderClass);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { cotBoardCache } from "./client";
|
|
3
3
|
import { cotHeadless } from "./headless";
|
|
4
|
-
import { COT_ROOT_OPTIONS, cotContractCode, cotContractCodeForTicker, cotRoot, cotUnknownMarketMessage } from "./model";
|
|
5
|
-
import { CotPane } from "./pane";
|
|
4
|
+
import { COT_ROOT_OPTIONS, cotContractCode, cotContractCodeForTicker, cotProxyFor, cotRoot, cotUnknownMarketMessage } from "./model";
|
|
6
5
|
import { COT_SERIES } from "./series";
|
|
7
6
|
|
|
8
7
|
export const cotModule: PluginModule = {
|
|
9
8
|
series: COT_SERIES,
|
|
10
|
-
panes: [{ id: "cot", name: "CFTC Positioning", icon: "P", component: CotPane,
|
|
9
|
+
panes: [{ id: "cot", name: "CFTC Positioning", icon: "P", get component(): typeof import("./pane").CotPane { const { CotPane } = require("./pane"); return CotPane; },
|
|
11
10
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 106, height: 30 },
|
|
12
11
|
tableExport: true, headless: cotHeadless }],
|
|
13
12
|
paneTemplates: [{ id: "cot-cot", paneId: "cot", label: "CFTC Positioning",
|
|
@@ -19,7 +18,9 @@ export const cotModule: PluginModule = {
|
|
|
19
18
|
const input = options?.arg?.trim();
|
|
20
19
|
const code = input ? cotContractCode(input) : cotContractCodeForTicker(context.activeTicker);
|
|
21
20
|
if (input && !code) throw new Error(cotUnknownMarketMessage(input));
|
|
22
|
-
|
|
21
|
+
// "jet fuel" opens heating oil, and the title says which market stands in.
|
|
22
|
+
const proxy = input ? cotProxyFor(input) : null;
|
|
23
|
+
return { title: code ? `COT ${cotRoot(code) ?? code}${proxy ? ` (${proxy.subject} proxy)` : ""}` : "CFTC Positioning", params: code ? { code } : undefined,
|
|
23
24
|
settings: { report: options?.values?.report ?? "legacy" }, placement: "floating" as const };
|
|
24
25
|
},
|
|
25
26
|
}],
|
|
@@ -2,10 +2,14 @@ import type { CotClass, CotContractPayload, CotFamily, CotHistoryPoint, CotPosit
|
|
|
2
2
|
import { staticSeries } from "../../../components/chart/static/series";
|
|
3
3
|
import type { ResolvedSeries } from "../../../time-series/types";
|
|
4
4
|
import type { PricePoint } from "../../../types/financials";
|
|
5
|
-
import { formatPriceObservation } from "../../../market-data/market/format";
|
|
5
|
+
import { formatMarketPriceWithCurrency, formatPriceObservation } from "../../../market-data/market/format";
|
|
6
|
+
import { futuresPricePer } from "../../../market-data/futures-contract";
|
|
7
|
+
import { unitCurrencyToken } from "../../../time-series/price-units";
|
|
6
8
|
import { cleanFloat32Price } from "../../../cli/history-rows";
|
|
7
9
|
import { cryptoPairCoin, isFuturesSymbol } from "../shared/crypto-pair";
|
|
8
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
bestNameMatch, FUTURES_CONTRACTS, futuresBoardContract, futuresBoardContractForTicker, futuresContractByCode, futuresContractByName, type FuturesContract,
|
|
12
|
+
} from "../../../market-data/futures-board";
|
|
9
13
|
|
|
10
14
|
export const COT_CLASSES: Record<CotFamily, Array<{ value: CotClass; label: string }>> = {
|
|
11
15
|
legacy: [{ value: "noncommercial", label: "Noncommercial" }, { value: "commercial", label: "Commercial" }, { value: "nonreportable", label: "Nonreportable" }],
|
|
@@ -162,7 +166,35 @@ export function cotContractCode(value: unknown): string | null {
|
|
|
162
166
|
if (alias) return alias;
|
|
163
167
|
const market = typedMarket(value);
|
|
164
168
|
if (market && "root" in market) return ROOTS[market.root]!.code;
|
|
165
|
-
|
|
169
|
+
if (market) return boardMarketCode(market.board);
|
|
170
|
+
const proxy = proxyMarket(value);
|
|
171
|
+
const proxied = proxy ? futuresContractByCode(proxy.root) : null;
|
|
172
|
+
return (proxied ? boardMarketCode(proxied) : null) ?? marketCode(input);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Markets the CFTC does not report, opened as the one traders price and hedge
|
|
176
|
+
* them by: jet fuel (kerosene) as NY Harbor ULSD, heating oil (HO), which
|
|
177
|
+
* airlines' fuel hedges and analysts follow it with. Opened this way, the
|
|
178
|
+
* pane's title and the report say which market stands in.
|
|
179
|
+
*/
|
|
180
|
+
const PROXY_MARKETS: ReadonlyArray<{ names: readonly string[]; root: string; subject: string }> = [
|
|
181
|
+
{ names: ["jet fuel", "jet fuels", "kerosene", "kerosine", "jet kerosene", "aviation fuel", "avtur"], root: "HO", subject: "jet fuel" },
|
|
182
|
+
];
|
|
183
|
+
function proxyMarket(value: string) {
|
|
184
|
+
const typed = value.trim().toLowerCase().replace(/[\s_-]+/g, " ");
|
|
185
|
+
return PROXY_MARKETS.find((proxy) => proxy.names.includes(typed)) ?? null;
|
|
186
|
+
}
|
|
187
|
+
/** What a typed name opens in place of a market the CFTC does not report ("jet fuel" opens HO), or null. */
|
|
188
|
+
export function cotProxyFor(value: unknown): { subject: string; root: string } | null {
|
|
189
|
+
if (typeof value !== "string" || typedMarket(value) || rootAlias(value.trim().toUpperCase())) return null;
|
|
190
|
+
const proxy = proxyMarket(value);
|
|
191
|
+
return proxy ? { subject: proxy.subject, root: proxy.root } : null;
|
|
192
|
+
}
|
|
193
|
+
/** The line a report opened through a proxy says: "Jet fuel has no CFTC market: heating oil (NY Harbor ULSD, HO) is the usual proxy." */
|
|
194
|
+
export function cotProxyNote(proxy: { subject: string; root: string }): string {
|
|
195
|
+
const subject = `${proxy.subject[0]!.toUpperCase()}${proxy.subject.slice(1)}`;
|
|
196
|
+
return `${subject} has no CFTC market: heating oil (NY Harbor ULSD, ${proxy.root}) is the usual proxy. Its price: gloom quote ${proxy.root}=F; `
|
|
197
|
+
+ "US jet fuel stocks (EIA, weekly): gloom fn G CAP:doe:jet";
|
|
166
198
|
}
|
|
167
199
|
/** The markets the unknown-market hint names, by the code and the word a newcomer knows them by. */
|
|
168
200
|
const SUGGESTED_MARKETS: ReadonlyArray<readonly [string, string]> = [
|
|
@@ -234,10 +266,27 @@ export function cotInteger(value: number | null, signed = false): string {
|
|
|
234
266
|
return value == null ? "--" : `${signed && value > 0 ? "+" : ""}${value.toLocaleString("en-US", { maximumFractionDigits: 0 })}`;
|
|
235
267
|
}
|
|
236
268
|
|
|
269
|
+
/**
|
|
270
|
+
* The front price's unit as a quote of the future writes it after the price:
|
|
271
|
+
* `USD/gal` for heating oil ($4.6218/gal), `USD` for gold, from the
|
|
272
|
+
* contract's unit ("USD / gallon"). Null where the unit names no currency
|
|
273
|
+
* (index points, Treasury points) or is unknown: the price reads bare.
|
|
274
|
+
*/
|
|
275
|
+
export function cotFrontPriceUnit(code: string, contractUnit: string | null | undefined): string | null {
|
|
276
|
+
const currency = contractUnit ? unitCurrencyToken(contractUnit.split("/")[0] ?? "") : null;
|
|
277
|
+
if (!currency) return null;
|
|
278
|
+
const root = cotRoot(code) ?? undefined;
|
|
279
|
+
return `${currency}${futuresPricePer({ root, spec: { unit: contractUnit } } as Parameters<typeof futuresPricePer>[0], currency)}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
237
282
|
/** Legend values stay exact: a compact 7.8K hides the futures price and -100K the net position. */
|
|
238
|
-
export function cotLegendValue(value: number, series: Pick<ResolvedSeries, "id">): string {
|
|
283
|
+
export function cotLegendValue(value: number, series: Pick<ResolvedSeries, "id"> & Partial<Pick<ResolvedSeries, "unit">>): string {
|
|
284
|
+
if (series.id !== "price") return cotInteger(value, true);
|
|
239
285
|
// Drop the float32 tail of provider bars without rounding away a 1/128 Treasury tick.
|
|
240
|
-
|
|
286
|
+
const price = cleanFloat32Price(value);
|
|
287
|
+
// In its currency and per its quantity, as a quote of the future reads: $4.6218/gal.
|
|
288
|
+
const [currency, per] = [series.unit?.split("/")[0]?.trim(), series.unit?.includes("/") ? `/${series.unit.split("/")[1]}` : ""];
|
|
289
|
+
return currency ? `${formatMarketPriceWithCurrency(price, currency, { assetCategory: "FUT" })}${per}` : formatPriceObservation(price);
|
|
241
290
|
}
|
|
242
291
|
|
|
243
292
|
/**
|
|
@@ -263,7 +312,9 @@ export function cotNetPoints(history: readonly CotHistoryPoint[], traderClass: C
|
|
|
263
312
|
}
|
|
264
313
|
return points;
|
|
265
314
|
}
|
|
266
|
-
|
|
315
|
+
/** `priceUnit` is the front price's (cotFrontPriceUnit), which its legend value is written in. */
|
|
316
|
+
export function cotChartSeries(data: CotContractPayload, traderClass: CotClass, price: readonly PricePoint[], colors: { positive: string; warning: string },
|
|
317
|
+
priceUnit: string | null = null): ResolvedSeries[] {
|
|
267
318
|
const series: ResolvedSeries[] = [{
|
|
268
319
|
...staticSeries(cotNetPoints(data.history, traderClass), { id: "net", label: `${COT_CLASSES[data.reportFamily].find((row) => row.value === traderClass)?.label ?? traderClass} net`, color: colors.warning, calendarSpaced: true }),
|
|
269
320
|
panelId: "net", unit: "contracts", unitGroup: "positions",
|
|
@@ -271,7 +322,7 @@ export function cotChartSeries(data: CotContractPayload, traderClass: CotClass,
|
|
|
271
322
|
// Net positioning is the subject, so it leads the legend and the strip; the front price follows.
|
|
272
323
|
if (price.length) series.push({
|
|
273
324
|
...staticSeries(price.map((row) => ({ date: row.date, observedAt: row.date, value: row.close })), { id: "price", label: "Front price", color: colors.positive, calendarSpaced: true }),
|
|
274
|
-
panelId: "price", unit: "", unitGroup: "price",
|
|
325
|
+
panelId: "price", unit: priceUnit ?? "", unitGroup: "price",
|
|
275
326
|
});
|
|
276
327
|
return series;
|
|
277
328
|
}
|
|
@@ -12,7 +12,7 @@ import { Box } from "../../../ui";
|
|
|
12
12
|
import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
13
13
|
import { compareSortValues, nextHeaderSort, type SortPreference } from "../../../utils/sort-values";
|
|
14
14
|
import { useResearchCloudSession } from "../shared/research-cloud-session";
|
|
15
|
-
import { cachedCotBoard, loadCotBoard, loadCotDetail } from "./client";
|
|
15
|
+
import { cachedCotBoard, loadCotBoard, loadCotDetail, loadCotPriceUnit } from "./client";
|
|
16
16
|
import { COT_CLASSES, COT_FAMILIES, COT_MAJOR_CODES, COT_POSITIONS, COT_SCOPES, cotChartSeries, cotClass, cotContractUnitText, cotFamily, cotInteger, cotLegendValue, cotMarketName, cotPositions, cotPositionsText, cotScope, type CotScope } from "./model";
|
|
17
17
|
|
|
18
18
|
const BOARD_COLUMNS: DataTableColumn[] = [
|
|
@@ -159,10 +159,14 @@ function CotDetail({ width, height, focused, code, family, positions, traderClas
|
|
|
159
159
|
const session = useResearchCloudSession();
|
|
160
160
|
const loader = useCallback(() => loadCotDetail(code, family, undefined, positions), [code, family, positions, session.requestKey]);
|
|
161
161
|
const resource = useAsyncResource(loader, { clearOnError: isAccessDenied });
|
|
162
|
+
// The front price's unit joins the legend once the contract terms answer, or
|
|
163
|
+
// the catalog's after a short wait: the chart draws without waiting for it.
|
|
164
|
+
const unitLoader = useCallback(() => loadCotPriceUnit(code), [code]);
|
|
165
|
+
const priceUnit = useAsyncResource(unitLoader).data ?? null;
|
|
162
166
|
const data = resource.data;
|
|
163
167
|
const payload = data?.payload;
|
|
164
168
|
const current = payload?.positions.find((row) => row.id === traderClass);
|
|
165
|
-
const series = useMemo(() => data ? cotChartSeries(data.payload, traderClass, data.price, colors) : [], [data, traderClass, colors]);
|
|
169
|
+
const series = useMemo(() => data ? cotChartSeries(data.payload, traderClass, data.price, colors, priceUnit) : [], [data, traderClass, colors, priceUnit]);
|
|
166
170
|
// The selected class row already carries the net and both ranks, and the
|
|
167
171
|
// legend names the class it charts, so the detail opens on the chart. A rank
|
|
168
172
|
// over a short history is a limitation, so it sits behind the warning.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
|
+
import { sourceDidNotAnswer } from "../../../api-client/errors";
|
|
2
3
|
import type { CpiBoardPayload, CpiRow } from "../../../api-client/cpi";
|
|
3
|
-
import { ApiRequestError } from "../../../api-client/errors";
|
|
4
4
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
5
|
-
import { cachedCloudResource, loadCloudResource } from "../shared/cloud-resource";
|
|
5
|
+
import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
6
6
|
|
|
7
7
|
/** What the pane says while the server has no months to serve: before the backend ships, or before its first read. */
|
|
8
8
|
export const CPI_NOT_AVAILABLE = "US consumer prices are not available yet.";
|
|
@@ -54,11 +54,12 @@ export async function fetchCpiBoard(client: Pick<typeof apiClient, "getCloudCpiB
|
|
|
54
54
|
try {
|
|
55
55
|
payload = await client.getCloudCpiBoard();
|
|
56
56
|
} catch (error) {
|
|
57
|
-
|
|
58
|
-
throw error;
|
|
57
|
+
throw unavailableOnServer(error, CPI_NOT_AVAILABLE, [404, 503]);
|
|
59
58
|
}
|
|
60
59
|
const board = validateCpiBoard(payload);
|
|
61
|
-
|
|
60
|
+
// The server's source did not answer this time: a saved copy answers, as it does a 5xx.
|
|
61
|
+
if (board.status === "unavailable") throw sourceDidNotAnswer(CPI_NOT_AVAILABLE);
|
|
62
|
+
if (!board.release.period || !board.rows.length) throw new Error(CPI_NOT_AVAILABLE);
|
|
62
63
|
return board;
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -3,7 +3,6 @@ import type { PluginModule } from "../plugin-module";
|
|
|
3
3
|
import { cpiBoardCache } from "./client";
|
|
4
4
|
import { cpiHeadless } from "./headless";
|
|
5
5
|
import { CPI_ROW_OPTIONS, cpiRowOption } from "./model";
|
|
6
|
-
import { CpiPane } from "./pane";
|
|
7
6
|
|
|
8
7
|
/** Opens on the table, or on the component typed after the mnemonic. */
|
|
9
8
|
function cpiInstance(options?: PaneTemplateCreateOptions): PaneTemplateInstanceConfig {
|
|
@@ -14,7 +13,7 @@ function cpiInstance(options?: PaneTemplateCreateOptions): PaneTemplateInstanceC
|
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
export const cpiModule: PluginModule = {
|
|
17
|
-
panes: [{ id: "cpi", name: "US Consumer Prices", icon: "C", component: CpiPane,
|
|
16
|
+
panes: [{ id: "cpi", name: "US Consumer Prices", icon: "C", get component(): typeof import("./pane").CpiPane { const { CpiPane } = require("./pane"); return CpiPane; },
|
|
18
17
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 100, height: 36 },
|
|
19
18
|
tableExport: true, headless: cpiHeadless }],
|
|
20
19
|
paneTemplates: [{
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
formatLevel,
|
|
33
33
|
formatMove,
|
|
34
34
|
partialDayText,
|
|
35
|
+
SETTLEMENT_TEXT,
|
|
35
36
|
type CdxRow,
|
|
36
37
|
} from "./model";
|
|
37
38
|
|
|
@@ -69,7 +70,7 @@ function IndexDetail({ row, width, height, focused }: { row: CdxRow; width: numb
|
|
|
69
70
|
const through = day.partial && day.changeDate ? `to ${day.changeDate}` : undefined;
|
|
70
71
|
const items: StatItem[] = [
|
|
71
72
|
{ id: "level", label: index.quote === "spread" ? "5Y spread" : "5Y price", value: formatLevel(index.quote, index.level),
|
|
72
|
-
detail:
|
|
73
|
+
detail: [formatPercentileRank(year.percentile, "1Y"), index.date ?? "--", ...(index.kind === "settlement" ? [SETTLEMENT_TEXT] : [])].join(" · ") },
|
|
73
74
|
...(index.quote === "price" && index.spreadBp !== undefined ? [{ id: "spread", label: "Spread eq", value: formatLevel("spread", index.spreadBp ?? null),
|
|
74
75
|
...(index.recovery != null ? { detail: `${Math.round(index.recovery * 100)}% recovery` } : {}) }] : []),
|
|
75
76
|
{ id: "1d", label: "1D", value: formatMove(index.quote, day.change1D), ...(through ? { detail: through } : {}) },
|
|
@@ -84,8 +84,10 @@ export type PriceHistoryLoader = (symbol: string) => Promise<PricePoint[]>;
|
|
|
84
84
|
|
|
85
85
|
/** Closes a month back are yesterday's news for half an hour. */
|
|
86
86
|
const FX_HISTORY_TTL_MS = 30 * 60_000;
|
|
87
|
-
/** Twenty-odd currencies
|
|
87
|
+
/** Twenty-odd currencies, a few at a time from a source that takes them one by one. */
|
|
88
88
|
const FX_HISTORY_CONCURRENCY = 4;
|
|
89
|
+
/** All at once from a source that batches them: the API client sends them sixteen a request. */
|
|
90
|
+
const FX_HISTORY_BATCHED_CONCURRENCY = 32;
|
|
89
91
|
const fxMoves = createTtlCache<number | null>({ ttlMs: FX_HISTORY_TTL_MS });
|
|
90
92
|
|
|
91
93
|
/** A load that fails reads as unknown for everyone waiting on it and is asked again next time, never remembered as unknown. */
|
|
@@ -97,10 +99,13 @@ function currencyMove(symbol: string, dollarsPerUnit: boolean, loadHistory: Pric
|
|
|
97
99
|
/**
|
|
98
100
|
* Each currency's 1M move against the dollar from its daily closes, shared
|
|
99
101
|
* across panes. A currency with no pair or no history reads as unknown.
|
|
102
|
+
* `batched`: `loadHistory` asks a source that batches them
|
|
103
|
+
* (`DataProvider.batchesReads`) with `batch`, so they are all asked at once.
|
|
100
104
|
*/
|
|
101
105
|
export async function loadCurrencyMoves(
|
|
102
106
|
currencies: readonly string[],
|
|
103
107
|
loadHistory: PriceHistoryLoader,
|
|
108
|
+
batched = false,
|
|
104
109
|
): Promise<Map<string, number | null>> {
|
|
105
110
|
const queue = [...new Set(currencies)];
|
|
106
111
|
const moves = new Map<string, number | null>();
|
|
@@ -110,6 +115,6 @@ export async function loadCurrencyMoves(
|
|
|
110
115
|
moves.set(currency, pair ? await currencyMove(pair.symbol, pair.dollarsPerUnit, loadHistory) : null);
|
|
111
116
|
}
|
|
112
117
|
};
|
|
113
|
-
await Promise.all(Array.from({ length: Math.min(FX_HISTORY_CONCURRENCY, queue.length) }, worker));
|
|
118
|
+
await Promise.all(Array.from({ length: Math.min(batched ? FX_HISTORY_BATCHED_CONCURRENCY : FX_HISTORY_CONCURRENCY, queue.length) }, worker));
|
|
114
119
|
return moves;
|
|
115
120
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneRow } from "../../../types/plugin";
|
|
2
2
|
import type { CdxBoardIndex, CreditIndexQuote } from "../../../api-client/credit-boards";
|
|
3
3
|
import { CDX_MAX_DAYS, fetchCdxBoard, fetchSovrBoard, loadCurrencyMoves } from "./client";
|
|
4
|
+
import { readsInBatches } from "../../../market-data/batched-reads";
|
|
4
5
|
import {
|
|
5
6
|
cdxDay,
|
|
6
7
|
cdxRows,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
formatCurrencyMove,
|
|
11
12
|
formatLevel,
|
|
12
13
|
formatMove,
|
|
14
|
+
levelKinds,
|
|
13
15
|
partialDayText,
|
|
14
16
|
quoteUnits,
|
|
15
17
|
sovrRows,
|
|
@@ -74,6 +76,8 @@ function historySection(index: CdxBoardIndex, days: number, now: number) {
|
|
|
74
76
|
const priced = index.quote === "price" && index.points.some((point) => point.spreadBp !== undefined);
|
|
75
77
|
const partial = today.partial && latest === index.date;
|
|
76
78
|
const symbol = cdxSeries(index.id);
|
|
79
|
+
const shown = index.points.filter((point) => point.date > from).reverse();
|
|
80
|
+
const kinds = levelKinds(shown, (point) => point.kind);
|
|
77
81
|
return {
|
|
78
82
|
title: `${index.name} 5Y daily ${index.quote === "spread" ? "spread" : "price"}`,
|
|
79
83
|
columns: [
|
|
@@ -83,11 +87,12 @@ function historySection(index: CdxBoardIndex, days: number, now: number) {
|
|
|
83
87
|
{ key: "prints", header: "Prints", align: "right" as const },
|
|
84
88
|
{ key: "maturity", header: "Contract", textFormat: (value: unknown) => formatContract(typeof value === "string" ? value : null) },
|
|
85
89
|
...(partial ? [dayColumn] : []),
|
|
90
|
+
...kinds.columns,
|
|
86
91
|
],
|
|
87
|
-
rows:
|
|
92
|
+
rows: shown.map((point) => ({
|
|
88
93
|
day: point.date, level: point.level, unit,
|
|
89
94
|
...(priced ? { spreadBp: point.spreadBp ?? null, spreadUnit: "bp" } : {}),
|
|
90
|
-
prints: point.prints, maturity: point.maturity, series: symbol,
|
|
95
|
+
prints: point.prints, maturity: point.maturity, series: symbol, ...kinds.row(point),
|
|
91
96
|
...(partial && point.date === latest ? { session: partialDayText(point.prints, today.lastPrintAt), partial: true, lastPrintAt: today.lastPrintAt } : {}),
|
|
92
97
|
})),
|
|
93
98
|
rowUnitKey: "unit",
|
|
@@ -133,6 +138,7 @@ export const cdxHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
133
138
|
const shown = index ? [index] : payload.indexes;
|
|
134
139
|
// A server that predates the spread equivalent sends none: no column of blanks.
|
|
135
140
|
const spreads = shown.some((entry) => entry.spreadBp !== undefined);
|
|
141
|
+
const kinds = levelKinds(shown, (entry) => entry.kind);
|
|
136
142
|
const rows = cdxRows(shown, payload.asOf, now).map((row) => {
|
|
137
143
|
const units = quoteUnits(row.index.quote);
|
|
138
144
|
const priced = spreads && row.index.quote === "price";
|
|
@@ -151,6 +157,7 @@ export const cdxHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
151
157
|
contract: formatContract(row.index.maturity),
|
|
152
158
|
date: row.index.date,
|
|
153
159
|
changeDate: row.day.changeDate,
|
|
160
|
+
...kinds.row(row.index),
|
|
154
161
|
...(row.day.partial ? { session: partialDayText(row.index.prints, row.day.lastPrintAt), partial: true, lastPrintAt: row.day.lastPrintAt } : {}),
|
|
155
162
|
};
|
|
156
163
|
});
|
|
@@ -158,7 +165,7 @@ export const cdxHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
158
165
|
return {
|
|
159
166
|
sections: [
|
|
160
167
|
{
|
|
161
|
-
title: "On the run, 5Y", columns: [...CDX_COLUMNS.filter((column) => spreads || column !== spreadColumn), ...(anyPartial ? [dayColumn] : [])], rows,
|
|
168
|
+
title: "On the run, 5Y", columns: [...CDX_COLUMNS.filter((column) => spreads || column !== spreadColumn), ...(anyPartial ? [dayColumn] : []), ...kinds.columns], rows,
|
|
162
169
|
rowUnitKey: "unit", rowSymbolKey: "series", rowAsOfKey: "date", interval: "daily",
|
|
163
170
|
},
|
|
164
171
|
...(index ? [historySection(index, days, now)] : []),
|
|
@@ -170,6 +177,15 @@ export const cdxHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
170
177
|
},
|
|
171
178
|
};
|
|
172
179
|
|
|
180
|
+
const SOVR_COLUMNS: HeadlessPaneColumn[] = [
|
|
181
|
+
{ key: "name", header: "Sovereign" },
|
|
182
|
+
levelColumn,
|
|
183
|
+
moveColumn("change1M", "1M", "unit"),
|
|
184
|
+
{ key: "currency", header: "Ccy 1M", align: "right" },
|
|
185
|
+
{ key: "percentile", header: "Pctl 1Y", align: "right", format: percentile },
|
|
186
|
+
{ key: "date", header: "As of" },
|
|
187
|
+
];
|
|
188
|
+
|
|
173
189
|
export const sovrHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
174
190
|
discovery: { aliases: ["SOVR", "WCDS"], dataRequirements: ["Gloom Cloud sovereign CDS endpoint"],
|
|
175
191
|
limitations: ["Daily 5Y levels from public DTCC prints; thinly traded names can miss days."] },
|
|
@@ -178,14 +194,7 @@ export const sovrHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
178
194
|
argument: { kind: "free-text", optional: true, placeholder: "country",
|
|
179
195
|
description: "A country (Brazil, BR) to list its row alone; omit for every sovereign." },
|
|
180
196
|
options: [],
|
|
181
|
-
columns:
|
|
182
|
-
{ key: "name", header: "Sovereign" },
|
|
183
|
-
levelColumn,
|
|
184
|
-
moveColumn("change1M", "1M", "unit"),
|
|
185
|
-
{ key: "currency", header: "Ccy 1M", align: "right" },
|
|
186
|
-
{ key: "percentile", header: "Pctl 1Y", align: "right", format: percentile },
|
|
187
|
-
{ key: "date", header: "As of" },
|
|
188
|
-
],
|
|
197
|
+
columns: SOVR_COLUMNS,
|
|
189
198
|
describe: "Sovereign CDS",
|
|
190
199
|
async load(args, ctx) {
|
|
191
200
|
const payload = await fetchSovrBoard(ctx.apiClient);
|
|
@@ -193,10 +202,13 @@ export const sovrHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
193
202
|
const country = query ? findSovereign(payload.sovereigns, query) : null;
|
|
194
203
|
if (query && !country) throw new Error(`No sovereign ${query}. Use one of: ${payload.sovereigns.map((entry) => entry.name).join(", ")}.`);
|
|
195
204
|
const sovereigns = country ? [country] : payload.sovereigns;
|
|
205
|
+
const batch = await readsInBatches(ctx.marketData, "getPriceHistory");
|
|
196
206
|
const moves = await loadCurrencyMoves(sovereigns.map((row) => row.currency),
|
|
197
|
-
(symbol) => ctx.marketData.getPriceHistory(symbol, "", "3M"));
|
|
207
|
+
(symbol) => ctx.marketData.getPriceHistory(symbol, "", "3M", batch ? { batch } : undefined), batch);
|
|
198
208
|
const publisher = boardPublisher(payload.source);
|
|
209
|
+
const kinds = levelKinds(sovereigns, (row) => row.kind);
|
|
199
210
|
return {
|
|
211
|
+
...(kinds.columns.length ? { columns: [...SOVR_COLUMNS, ...kinds.columns] } : {}),
|
|
200
212
|
rows: sovrRows(sovereigns, payload.asOf, moves).map((row) => ({
|
|
201
213
|
name: row.label,
|
|
202
214
|
series: sovereignSeries(row.sovereign.id),
|
|
@@ -206,6 +218,7 @@ export const sovrHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
206
218
|
currency: formatCurrencyMove(row.sovereign.currency, row.currencyMove),
|
|
207
219
|
percentile: rank(row.percentile),
|
|
208
220
|
date: row.sovereign.date,
|
|
221
|
+
...kinds.row(row.sovereign),
|
|
209
222
|
})),
|
|
210
223
|
rowUnitKey: "unit",
|
|
211
224
|
rowSymbolKey: "series",
|
|
@@ -2,12 +2,14 @@ import type {
|
|
|
2
2
|
CdxBoardIndex,
|
|
3
3
|
CreditBoardPoint,
|
|
4
4
|
CreditIndexQuote,
|
|
5
|
+
CreditLevelKind,
|
|
5
6
|
SovrBoardSovereign,
|
|
6
7
|
} from "../../../api-client/credit-boards";
|
|
7
8
|
import { spanAxisFormatter, formatBpAxis } from "../../../components/chart-table/axis";
|
|
8
9
|
import { historyStatistics } from "../../../components/chart/curve/model";
|
|
9
10
|
import type { MarketBoardRow } from "../../../components/market-board";
|
|
10
11
|
import type { PricePoint } from "../../../types/financials";
|
|
12
|
+
import type { HeadlessPaneColumn } from "../../../types/plugin";
|
|
11
13
|
|
|
12
14
|
const YEAR_DAYS = 365;
|
|
13
15
|
/** A level older than this is not the current market. */
|
|
@@ -93,6 +95,33 @@ function utcClock(instant: string): string | null {
|
|
|
93
95
|
return Number.isFinite(time) ? new Date(time).toISOString().slice(11, 16) : null;
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
/**
|
|
99
|
+
* How a level that is the day's settlement price, not its trades, is marked
|
|
100
|
+
* beside its date.
|
|
101
|
+
*/
|
|
102
|
+
export const SETTLEMENT_TEXT = "settlement";
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A report's mark for the levels that are settlement prices: blank for a
|
|
106
|
+
* level from the day's trades. Shown only when a row is one, and each row
|
|
107
|
+
* then carries `kind`.
|
|
108
|
+
*/
|
|
109
|
+
const levelKindColumn: HeadlessPaneColumn = {
|
|
110
|
+
key: "kind", header: "Level",
|
|
111
|
+
description: "Blank for a level from the day's trades. settlement for the day's end-of-day settlement price, which stands in "
|
|
112
|
+
+ "on a day without enough agreeing trades; its prints are 0.",
|
|
113
|
+
format: (value) => value === "settlement" ? SETTLEMENT_TEXT : "",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** `kind` on every row once one level is a settlement; nothing otherwise, so a report without one is unchanged. */
|
|
117
|
+
export function levelKinds<T>(items: readonly T[], kindOf: (item: T) => CreditLevelKind | null | undefined) {
|
|
118
|
+
const shown = items.some((item) => kindOf(item) === "settlement");
|
|
119
|
+
return {
|
|
120
|
+
columns: shown ? [levelKindColumn] : [],
|
|
121
|
+
row: (item: T) => shown ? { kind: kindOf(item) ?? "traded" } : {},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
96
125
|
/**
|
|
97
126
|
* What a day still trading rests on: `so far, 38 prints, last 08:09 UTC`.
|
|
98
127
|
* The print time is left out when the server does not say it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CreditBoardPoint } from "../../../api-client/credit-boards";
|
|
2
2
|
import type { SeriesDefinition, SeriesLoadResult } from "../../../series/types";
|
|
3
|
-
import {
|
|
3
|
+
import { cachedSeriesNote } from "../shared/series-warning";
|
|
4
4
|
import { loadCdxBoard, loadSovrBoard } from "./client";
|
|
5
5
|
|
|
6
6
|
/** The five on-the-run 5Y index contracts the CDX tab lists; IG and iTraxx quote spread, HY and EM price. */
|
|
@@ -27,14 +27,13 @@ function boardPoints(
|
|
|
27
27
|
rows: ReadonlyArray<{ id: string; points: readonly CreditBoardPoint[] }>,
|
|
28
28
|
id: string,
|
|
29
29
|
label: string,
|
|
30
|
-
resource: { stale?: boolean; refreshError?: string | null },
|
|
30
|
+
resource: { stale?: boolean; refreshError?: string | null; savedAt?: number },
|
|
31
31
|
): SeriesLoadResult {
|
|
32
32
|
const row = rows.find((entry) => entry.id === id);
|
|
33
33
|
if (!row) throw new Error(`${label} is not on the board right now.`);
|
|
34
|
-
const warning = cachedSeriesWarning(resource);
|
|
35
34
|
return {
|
|
36
35
|
points: row.points.map((point) => ({ date: point.date, value: point.level })),
|
|
37
|
-
...(
|
|
36
|
+
...cachedSeriesNote(resource),
|
|
38
37
|
};
|
|
39
38
|
}
|
|
40
39
|
|
|
@@ -25,12 +25,14 @@ import { formatAsOf } from "../cds/model";
|
|
|
25
25
|
import { FIXED_INCOME_PANE_ID } from "../fixed-income/model";
|
|
26
26
|
import { useCommandValue } from "../shared/command-tab";
|
|
27
27
|
import { getCachedSovrBoard, loadCurrencyMoves, loadSovrBoard } from "./client";
|
|
28
|
+
import { readsInBatches } from "../../../market-data/batched-reads";
|
|
28
29
|
import {
|
|
29
30
|
findSovereign,
|
|
30
31
|
formatContract,
|
|
31
32
|
formatCurrencyMove,
|
|
32
33
|
formatLevel,
|
|
33
34
|
formatMove,
|
|
35
|
+
SETTLEMENT_TEXT,
|
|
34
36
|
sovrRows,
|
|
35
37
|
type SovrRow,
|
|
36
38
|
} from "./model";
|
|
@@ -65,7 +67,7 @@ function SovereignDetail({ row, width, height, focused }: { row: SovrRow; width:
|
|
|
65
67
|
const { sovereign, year } = row;
|
|
66
68
|
const items: StatItem[] = [
|
|
67
69
|
{ id: "level", label: "5Y spread", value: formatLevel("spread", sovereign.level),
|
|
68
|
-
detail:
|
|
70
|
+
detail: [formatPercentileRank(year.percentile, "1Y"), sovereign.date, ...(sovereign.kind === "settlement" ? [SETTLEMENT_TEXT] : [])].join(" · ") },
|
|
69
71
|
{ id: "1m", label: "1M", value: formatMove("spread", sovereign.change1M) },
|
|
70
72
|
{ id: "1w", label: "1W", value: formatMove("spread", sovereign.change1W) },
|
|
71
73
|
{ id: "fx", label: "Currency 1M", value: formatCurrencyMove(sovereign.currency, row.currencyMove) },
|
|
@@ -99,9 +101,12 @@ export function SovrPane({ focused, width, height }: PaneProps) {
|
|
|
99
101
|
// Each currency's month against the dollar, from the same daily closes the FX panes use.
|
|
100
102
|
const currencies = useMemo(() => [...new Set(payload?.sovereigns.map((row) => row.currency) ?? [])].sort().join(","),
|
|
101
103
|
[payload]);
|
|
102
|
-
const movesLoader = useCallback(() =>
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
const movesLoader = useCallback(async () => {
|
|
105
|
+
if (!provider || !currencies) return NO_MOVES;
|
|
106
|
+
// Every currency at once, in batch requests, from a source that batches them; a few at a time otherwise.
|
|
107
|
+
const batch = await readsInBatches(provider, "getPriceHistory");
|
|
108
|
+
return loadCurrencyMoves(currencies.split(","), (symbol) => provider.getPriceHistory(symbol, "", "3M", batch ? { batch } : undefined), batch);
|
|
109
|
+
}, [currencies, provider]);
|
|
105
110
|
const moves = useAsyncResource(movesLoader, { keepPreviousData: true });
|
|
106
111
|
const rows = useMemo(() => payload ? sovrRows(payload.sovereigns, payload.asOf, moves.data ?? NO_MOVES) : EMPTY_ROWS,
|
|
107
112
|
[moves.data, payload]);
|
|
@@ -66,20 +66,28 @@ async function loadSeries(
|
|
|
66
66
|
loader: CreditSeriesLoader,
|
|
67
67
|
) {
|
|
68
68
|
const request = requestFor(definition.seriesId);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
69
|
+
let result: Awaited<ReturnType<typeof loadCachedFredSeries>>;
|
|
70
|
+
try {
|
|
71
|
+
result = await loadCachedFredSeries(
|
|
72
|
+
request,
|
|
73
|
+
async () => {
|
|
74
|
+
const data = trimHistory(await loader(definition.seriesId, {
|
|
75
|
+
limit: HISTORY_LIMIT,
|
|
76
|
+
sortOrder: "desc",
|
|
77
|
+
}));
|
|
78
|
+
// Reject incompatible source responses before they replace usable cache.
|
|
79
|
+
normalizeCreditSeries(definition, data);
|
|
80
|
+
return data;
|
|
81
|
+
},
|
|
82
|
+
{ force },
|
|
83
|
+
);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// A refusal or a reply that fails validation is no saved copy's to answer: it is this row's
|
|
86
|
+
// error, beside the dated prior value already read, which stays as stale rather than vanish.
|
|
87
|
+
const kept = getCachedFredSeries(request, { allowExpired: true });
|
|
88
|
+
if (!kept) throw error;
|
|
89
|
+
return { row: normalizeCreditSeries(definition, kept.data, true), refreshError: error instanceof Error ? error.message : String(error) };
|
|
90
|
+
}
|
|
83
91
|
return {
|
|
84
92
|
row: normalizeCreditSeries(definition, result.data, result.stale),
|
|
85
93
|
refreshError: result.refreshError,
|