gloomberb 0.16.4 → 0.16.5
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 +14 -91
- package/package.json +4 -1
- package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
- package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
- package/src/account/billing.ts +57 -0
- package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
- package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
- package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
- package/src/account/session.ts +16 -0
- package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
- package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
- package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
- package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
- package/src/api-client/data.ts +24 -2
- package/src/api-client/earnings.ts +6 -0
- package/src/api-client/futures-curve.ts +56 -0
- package/src/api-client/index.ts +4 -1
- package/src/api-client/market-discovery.ts +10 -1
- package/src/api-client/paths.ts +7 -0
- package/src/api-client/research-activity.ts +15 -10
- package/src/api-client/types.ts +17 -1
- package/src/api-client/yield-curves.ts +8 -0
- package/src/app/global-shortcuts.ts +15 -1
- package/src/app/pane-runtime/ticker-open-runtime.ts +7 -3
- package/src/architecture/core-builtin-allowlist.json +4 -13
- package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
- package/src/capabilities/chart-series.ts +11 -1
- package/src/cli/commands/crypto-hints.ts +7 -0
- package/src/cli/commands/fred-series.ts +61 -0
- package/src/cli/commands/market.ts +407 -104
- package/src/cli/commands/news-hints.ts +62 -0
- package/src/cli/commands/news-search.ts +53 -0
- package/src/cli/commands/options-leaps.ts +5 -2
- package/src/cli/commands/overview.ts +93 -20
- package/src/cli/commands/remote.ts +2 -1
- package/src/cli/commands/search.ts +82 -9
- package/src/cli/commands/system.ts +55 -8
- package/src/cli/commands/ticker.ts +88 -11
- package/src/cli/context.ts +45 -14
- package/src/cli/desktop-pane-shot.ts +59 -7
- package/src/cli/earnings-calendar.ts +71 -0
- package/src/cli/freshness.ts +18 -7
- package/src/cli/glossary.ts +429 -0
- package/src/cli/history-rows.ts +48 -7
- package/src/cli/index-roots.ts +7 -0
- package/src/cli/index.ts +21 -7
- package/src/cli/known-gaps.ts +27 -0
- package/src/cli/listing-arg.ts +417 -56
- package/src/cli/not-a-ticker.ts +12 -5
- package/src/cli/pane-functions/access-gate.ts +5 -0
- package/src/cli/pane-functions/capabilities.ts +3 -2
- package/src/cli/pane-functions/catalog.ts +35 -5
- package/src/cli/pane-functions/data.ts +12 -8
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.ts +20 -6
- package/src/cli/pane-functions/freshness.ts +363 -43
- package/src/cli/pane-functions/function-help.ts +12 -5
- package/src/cli/pane-functions/headless.ts +45 -17
- package/src/cli/pane-functions/index.ts +63 -10
- package/src/cli/pane-functions/market-session.ts +22 -8
- package/src/cli/pane-functions/options.ts +63 -22
- package/src/cli/pane-functions/report-notices.ts +25 -7
- package/src/cli/pane-functions/report.ts +15 -8
- package/src/cli/pane-functions/resolver.ts +49 -12
- package/src/cli/pane-functions/screenshot.ts +110 -23
- package/src/cli/report-tables.ts +3 -0
- package/src/cli/result.ts +4 -1
- package/src/cli/staged-tickers.ts +52 -0
- package/src/cli/symbol-args.ts +9 -0
- package/src/cli/symbol-list.ts +172 -0
- package/src/cli/ticker-resolution.ts +223 -30
- package/src/cli/types.ts +5 -0
- package/src/components/chart/composite/axis-overlays.tsx +6 -2
- package/src/components/chart/composite/composite-chart.tsx +40 -6
- package/src/components/chart/composite/legend.tsx +49 -2
- package/src/components/chart/composite/panel-surface.tsx +44 -17
- package/src/components/chart/composite/price-axis-labels.tsx +3 -1
- package/src/components/command-bar/panel/index.tsx +11 -3
- package/src/components/command-bar/panel/runtime.ts +3 -0
- package/src/components/command-bar/panel/types.ts +2 -0
- package/src/components/command-bar/routes/root/provider-search.ts +4 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
- package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
- package/src/components/command-bar/workflow/tickers.ts +23 -4
- package/src/components/layout/detached-pane-shell.tsx +9 -0
- package/src/components/layout/floating-pane.tsx +3 -0
- package/src/components/layout/header.tsx +20 -5
- package/src/components/layout/pane/content.tsx +7 -13
- package/src/components/layout/pane/footer/index.tsx +6 -2
- package/src/components/layout/pane/footer/model.ts +35 -1
- package/src/components/layout/pane/header.tsx +24 -1
- package/src/components/layout/pane/index.tsx +12 -2
- package/src/components/layout/pane/sidebar.tsx +78 -10
- package/src/components/layout/shell/active-drag.ts +21 -5
- package/src/components/layout/shell/drag/index.ts +39 -9
- package/src/components/layout/shell/drag/live.ts +61 -51
- package/src/components/layout/shell/drag/runtime.ts +54 -12
- package/src/components/layout/shell/index.tsx +25 -7
- package/src/components/layout/shell/menu.ts +5 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
- package/src/components/layout/shell/pane/layers.tsx +192 -45
- package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
- package/src/components/markdown-text.tsx +62 -20
- package/src/components/metric-treemap/heat-surface.tsx +10 -1
- package/src/components/onboarding/account-step/form-panel.tsx +1 -1
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-keyboard.ts +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +2 -2
- package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
- package/src/components/onboarding/wizard-account.ts +2 -2
- package/src/components/pane-template-wizard.tsx +9 -1
- package/src/components/price-selector-dialog.tsx +11 -2
- package/src/components/sign-in-gate.tsx +3 -3
- package/src/components/ticker/list-table-view.tsx +24 -1
- package/src/components/ui/action-row.tsx +2 -1
- package/src/components/ui/button.tsx +7 -1
- package/src/components/ui/choice-dialog.tsx +6 -4
- package/src/components/ui/confirm-dialog.tsx +28 -18
- package/src/components/ui/data-table/index.tsx +17 -6
- package/src/components/ui/data-table/opentui/index.tsx +37 -6
- package/src/components/ui/data-table/types.ts +12 -0
- package/src/components/ui/dialog-actions.tsx +64 -0
- package/src/components/ui/display.tsx +17 -3
- package/src/components/ui/frame.tsx +35 -2
- package/src/components/ui/icon.tsx +2 -0
- package/src/components/ui/text-prompt-dialog.tsx +11 -9
- package/src/data/cached-query.ts +2 -0
- package/src/data/config/store/normalize.ts +7 -2
- package/src/data/ticker-repository.ts +5 -0
- package/src/data/ticker-store.ts +15 -0
- package/src/docs/content.ts +215 -0
- package/src/docs/load.ts +139 -0
- package/src/docs/requests.ts +39 -0
- package/src/docs/snapshot.chunk.ts +8 -0
- package/src/docs/snapshot.json +7416 -0
- package/src/docs/topics.ts +216 -0
- package/src/i18n/es.ts +61 -4
- package/src/i18n/ja.ts +61 -4
- package/src/i18n/ko.ts +61 -4
- package/src/i18n/zh-cn.ts +61 -4
- package/src/i18n/zh-tw.ts +61 -4
- package/src/layout/desks.ts +1 -1
- package/src/layout/pane-manager/dock-tree.ts +18 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/market-data/coordinator/chart.ts +10 -4
- package/src/market-data/coordinator/index.ts +6 -3
- package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
- package/src/market-data/futures-contract.ts +151 -0
- package/src/market-data/hooks.tsx +10 -2
- package/src/market-data/market/format.ts +39 -21
- package/src/market-data/market/freshness.ts +81 -18
- package/src/market-data/market/status.ts +19 -23
- package/src/market-data/published-apac-sessions.ts +23 -1
- package/src/market-data/quotes/contributions.ts +3 -0
- package/src/market-data/quotes/metadata.ts +7 -24
- package/src/market-data/quotes/resolution.ts +2 -0
- package/src/market-data/quotes/screener-live-quotes.ts +5 -4
- package/src/market-data/result-types.ts +2 -0
- package/src/plugins/builtin/account-management/model.ts +1 -56
- package/src/plugins/builtin/account-management/pane.tsx +6 -4
- package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
- package/src/plugins/builtin/alerts/cli.ts +313 -47
- package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
- package/src/plugins/builtin/alerts/events.ts +23 -2
- package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +41 -9
- package/src/plugins/builtin/alerts/pane.tsx +8 -0
- package/src/plugins/builtin/alerts/research-builder.ts +8 -3
- package/src/plugins/builtin/alerts/research-rules.ts +18 -2
- package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
- package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
- package/src/plugins/builtin/alerts/scheduled.ts +254 -0
- package/src/plugins/builtin/alerts/targets.ts +92 -0
- package/src/plugins/builtin/analytics/index.tsx +4 -2
- package/src/plugins/builtin/analytics/risk-headless.ts +30 -12
- package/src/plugins/builtin/analytics/risk-math.ts +91 -27
- package/src/plugins/builtin/analytics/risk-model.ts +35 -5
- package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
- package/src/plugins/builtin/attention/index.tsx +2 -0
- package/src/plugins/builtin/attention/pane.tsx +3 -1
- package/src/plugins/builtin/attention/series.ts +26 -0
- package/src/plugins/builtin/awards/index.tsx +2 -0
- package/src/plugins/builtin/awards/pane.tsx +3 -1
- package/src/plugins/builtin/awards/series.ts +29 -0
- package/src/plugins/builtin/bond-calculator/model.ts +2 -1
- package/src/plugins/builtin/catalysts/pane.tsx +3 -1
- package/src/plugins/builtin/cds/client.ts +70 -13
- package/src/plugins/builtin/cds/headless.ts +116 -19
- package/src/plugins/builtin/cds/index.tsx +3 -1
- package/src/plugins/builtin/cds/model.ts +193 -31
- package/src/plugins/builtin/cds/pane.tsx +56 -14
- package/src/plugins/builtin/cds/series.ts +19 -0
- package/src/plugins/builtin/central-bank-rates/index.tsx +2 -0
- package/src/plugins/builtin/central-bank-rates/series.ts +42 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +192 -255
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
- package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
- package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
- package/src/plugins/builtin/chart-composer/headless.ts +8 -3
- package/src/plugins/builtin/chart-composer/index.tsx +9 -2
- package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
- package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
- package/src/plugins/builtin/chat/controller/index.ts +2 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
- package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
- package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
- package/src/plugins/builtin/cloud/askg/model.ts +27 -8
- package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
- package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
- package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
- package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
- package/src/plugins/builtin/company-attention/headless.ts +19 -5
- package/src/plugins/builtin/company-attention/index.tsx +2 -0
- package/src/plugins/builtin/company-attention/model.ts +26 -7
- package/src/plugins/builtin/company-attention/pane.tsx +12 -9
- package/src/plugins/builtin/company-attention/series.ts +52 -0
- package/src/plugins/builtin/company-kpis/pane.tsx +4 -2
- package/src/plugins/builtin/composite-plugins.ts +19 -13
- package/src/plugins/builtin/cot/index.tsx +3 -1
- package/src/plugins/builtin/cot/model.ts +40 -11
- package/src/plugins/builtin/cot/series.ts +27 -0
- package/src/plugins/builtin/credit-boards/index.tsx +2 -0
- package/src/plugins/builtin/credit-boards/series.ts +66 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -0
- package/src/plugins/builtin/credit-conditions/series.ts +26 -0
- package/src/plugins/builtin/credit-documents/headless.ts +31 -1
- package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
- package/src/plugins/builtin/crypto-board/headless.ts +2 -3
- package/src/plugins/builtin/crypto-board/index.tsx +2 -0
- package/src/plugins/builtin/crypto-board/model.ts +12 -0
- package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
- package/src/plugins/builtin/crypto-board/series.ts +48 -0
- package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/series.ts +45 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +2 -2
- package/src/plugins/builtin/dividend-yield/index.tsx +2 -0
- package/src/plugins/builtin/dividend-yield/series.ts +20 -0
- package/src/plugins/builtin/docs/blocks.tsx +181 -0
- package/src/plugins/builtin/docs/index.tsx +68 -0
- package/src/plugins/builtin/docs/pane.tsx +426 -0
- package/src/plugins/builtin/docs/targets.ts +72 -0
- package/src/plugins/builtin/docs/use-docs.ts +60 -0
- package/src/plugins/builtin/doe/index.tsx +2 -0
- package/src/plugins/builtin/doe/series.ts +62 -0
- package/src/plugins/builtin/earnings/client.ts +3 -2
- package/src/plugins/builtin/earnings/format.ts +1 -5
- package/src/plugins/builtin/earnings/headless.ts +58 -6
- package/src/plugins/builtin/earnings/history-model.ts +15 -1
- package/src/plugins/builtin/earnings/history-table.ts +13 -2
- package/src/plugins/builtin/earnings/history.tsx +7 -3
- package/src/plugins/builtin/econ/fred-catalog.ts +189 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
- package/src/plugins/builtin/econ/index.tsx +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
- package/src/plugins/builtin/exposure/holdings.ts +1 -1
- package/src/plugins/builtin/exposure/pane.tsx +3 -1
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/fear-greed/series.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +43 -3
- package/src/plugins/builtin/futures/model.ts +1 -1
- package/src/plugins/builtin/futures/returns.ts +1 -1
- package/src/plugins/builtin/futures/series.ts +13 -0
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +70 -9
- package/src/plugins/builtin/futures-curve/index.tsx +1 -1
- package/src/plugins/builtin/futures-curve/model.ts +66 -11
- package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
- package/src/plugins/builtin/fx-matrix/index.tsx +1 -1
- package/src/plugins/builtin/gpu/board.tsx +13 -10
- package/src/plugins/builtin/gpu/changes.tsx +4 -4
- package/src/plugins/builtin/gpu/client.ts +19 -1
- package/src/plugins/builtin/gpu/equities.tsx +14 -10
- package/src/plugins/builtin/gpu/headless.ts +56 -25
- package/src/plugins/builtin/gpu/history.tsx +3 -3
- package/src/plugins/builtin/gpu/index.tsx +2 -0
- package/src/plugins/builtin/gpu/model.ts +101 -16
- package/src/plugins/builtin/gpu/pane.tsx +28 -12
- package/src/plugins/builtin/gpu/series.ts +38 -0
- package/src/plugins/builtin/help/function-card.tsx +25 -6
- package/src/plugins/builtin/help/index.tsx +17 -8
- package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
- package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
- package/src/plugins/builtin/holders/client.ts +22 -3
- package/src/plugins/builtin/holders/format.ts +18 -3
- package/src/plugins/builtin/holders/headless.ts +36 -133
- package/src/plugins/builtin/holders/pane.tsx +11 -4
- package/src/plugins/builtin/holders/report-header.ts +40 -5
- package/src/plugins/builtin/holders/report-rows.ts +240 -0
- package/src/plugins/builtin/insider/client.ts +35 -20
- package/src/plugins/builtin/insider/headless.ts +41 -11
- package/src/plugins/builtin/insider/index.tsx +26 -9
- package/src/plugins/builtin/insider/insider-data.ts +10 -0
- package/src/plugins/builtin/insider/issuer.ts +49 -0
- package/src/plugins/builtin/insider/model.ts +3 -0
- package/src/plugins/builtin/iv-history/client.ts +14 -4
- package/src/plugins/builtin/iv-history/headless.ts +7 -4
- package/src/plugins/builtin/iv-history/index.tsx +2 -0
- package/src/plugins/builtin/iv-history/model.ts +111 -7
- package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
- package/src/plugins/builtin/iv-history/series.ts +29 -0
- package/src/plugins/builtin/jobs/headless.ts +28 -3
- package/src/plugins/builtin/jobs/index.tsx +3 -1
- package/src/plugins/builtin/jobs/series.ts +19 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
- package/src/plugins/builtin/macro-day/client.ts +44 -3
- package/src/plugins/builtin/macro-day/headless.ts +13 -10
- package/src/plugins/builtin/macro-day/model.ts +56 -10
- package/src/plugins/builtin/macro-day/pane.tsx +30 -20
- package/src/plugins/builtin/market-heatmap/model.ts +19 -11
- package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
- package/src/plugins/builtin/market-movers/client.ts +7 -5
- package/src/plugins/builtin/market-movers/index.tsx +3 -2
- package/src/plugins/builtin/market-movers/model.ts +30 -0
- package/src/plugins/builtin/market-movers/screener.ts +31 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
- package/src/plugins/builtin/market-valuation/index.tsx +3 -3
- package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
- package/src/plugins/builtin/members/headless.ts +7 -2
- package/src/plugins/builtin/members/model.ts +31 -3
- package/src/plugins/builtin/members/pane.tsx +3 -6
- package/src/plugins/builtin/mna/headless.ts +15 -3
- package/src/plugins/builtin/money-markets/index.tsx +2 -0
- package/src/plugins/builtin/money-markets/series.ts +41 -0
- package/src/plugins/builtin/notes/cli.ts +12 -7
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
- package/src/plugins/builtin/options-positioning/client.ts +7 -3
- package/src/plugins/builtin/options-positioning/headless.ts +1 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +2 -1
- package/src/plugins/builtin/options-scenario/headless.ts +1 -0
- package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
- package/src/plugins/builtin/perps/client.ts +40 -6
- package/src/plugins/builtin/perps/equity-row.tsx +33 -11
- package/src/plugins/builtin/perps/freshness.ts +66 -0
- package/src/plugins/builtin/perps/headless.ts +45 -30
- package/src/plugins/builtin/perps/index.tsx +18 -1
- package/src/plugins/builtin/perps/model.ts +20 -5
- package/src/plugins/builtin/perps/pane.tsx +76 -20
- package/src/plugins/builtin/perps/series.ts +76 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +67 -61
- package/src/plugins/builtin/plugin-module.ts +3 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +273 -0
- package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +331 -65
- package/src/plugins/builtin/portfolio-list/cli/render.ts +200 -31
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +24 -0
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -47
- package/src/plugins/builtin/portfolio-list/column-values.ts +1 -1
- package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +34 -4
- package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -1
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +17 -1
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +7 -1
- package/src/plugins/builtin/power/client.ts +16 -0
- package/src/plugins/builtin/power/evidence.ts +6 -2
- package/src/plugins/builtin/power/headless.ts +71 -7
- package/src/plugins/builtin/power/index.tsx +4 -1
- package/src/plugins/builtin/power/model.ts +72 -1
- package/src/plugins/builtin/power/pane.tsx +32 -9
- package/src/plugins/builtin/rate-path/headless.ts +11 -3
- package/src/plugins/builtin/realized-vol/headless.ts +5 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
- package/src/plugins/builtin/research/event-model.ts +68 -24
- package/src/plugins/builtin/research/events-headless.ts +2 -1
- package/src/plugins/builtin/research/headless.ts +2 -1
- package/src/plugins/builtin/seasonality/client.ts +15 -7
- package/src/plugins/builtin/seasonality/headless.ts +36 -12
- package/src/plugins/builtin/seasonality/index.tsx +2 -2
- package/src/plugins/builtin/seasonality/model.ts +119 -0
- package/src/plugins/builtin/seasonality/pane.tsx +108 -37
- package/src/plugins/builtin/series-inventory.ts +91 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
- package/src/plugins/builtin/shared/command-tab.ts +30 -12
- package/src/plugins/builtin/shared/earnings-report.ts +55 -0
- package/src/plugins/builtin/shared/fred-points.ts +20 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
- package/src/plugins/builtin/shared/open-description.ts +29 -0
- package/src/plugins/builtin/shared/report-freshness.ts +9 -0
- package/src/plugins/builtin/shared/series-warning.ts +5 -0
- package/src/plugins/builtin/short-interest/headless.ts +1 -0
- package/src/plugins/builtin/short-interest/index.tsx +2 -0
- package/src/plugins/builtin/short-interest/series.ts +36 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
- package/src/plugins/builtin/short-volume/headless.ts +1 -0
- package/src/plugins/builtin/short-volume/index.tsx +2 -0
- package/src/plugins/builtin/short-volume/series.ts +23 -0
- package/src/plugins/builtin/social-mentions/index.tsx +2 -0
- package/src/plugins/builtin/social-mentions/series.ts +23 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
- package/src/plugins/builtin/supply-chain/pane.tsx +3 -1
- package/src/plugins/builtin/thirteenf/api.ts +1 -0
- package/src/plugins/builtin/thirteenf/data.ts +6 -4
- package/src/plugins/builtin/thirteenf/headless.ts +217 -36
- package/src/plugins/builtin/thirteenf/model.ts +39 -1
- package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
- package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
- package/src/plugins/builtin/thirteenf/types.ts +2 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +48 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
- package/src/plugins/builtin/time-sales/headless.ts +1 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
- package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
- package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
- package/src/plugins/builtin/vol-surface/headless.ts +1 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -0
- package/src/plugins/builtin/volatility/series.ts +84 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
- package/src/plugins/builtin/world-venue-map/index.ts +2 -1
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +8 -4
- package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
- package/src/plugins/builtin/yield-curve/chart.ts +2 -2
- package/src/plugins/builtin/yield-curve/curves.ts +21 -8
- package/src/plugins/builtin/yield-curve/headless.ts +71 -18
- package/src/plugins/builtin/yield-curve/index.tsx +3 -1
- package/src/plugins/builtin/yield-curve/series.ts +86 -0
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/registry/index.ts +89 -4
- package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
- package/src/portfolio/position-value.ts +40 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
- package/src/public/time-series.ts +17 -0
- package/src/react/auto-refresh.ts +13 -3
- package/src/remote/app-host.tsx +20 -4
- package/src/remote/confirm-change.tsx +85 -0
- package/src/remote/controller-utils.ts +8 -1
- package/src/remote/controller.ts +123 -6
- package/src/remote/pane-screenshot.ts +47 -0
- package/src/remote/schema.ts +62 -0
- package/src/remote/types.ts +30 -0
- package/src/remote/watchlist.ts +306 -0
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/dom/data-table/frozen-column.ts +3 -1
- package/src/renderers/dom/data-table/index.tsx +33 -6
- package/src/renderers/dom/data-table/row.tsx +27 -1
- package/src/renderers/dom/desktop/control-styles.ts +63 -1
- package/src/renderers/dom/desktop/controls.tsx +30 -23
- package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -0
- package/src/renderers/dom/host/box.tsx +3 -0
- package/src/renderers/dom/host/chart-surface.tsx +6 -2
- package/src/renderers/dom/host/live-frame.ts +6 -2
- package/src/renderers/dom/host/overflow-fade.ts +14 -1
- package/src/renderers/dom/host/size-observer.ts +26 -0
- package/src/renderers/dom/host/surface-3d.tsx +7 -2
- package/src/renderers/dom/host/text.tsx +1 -1
- package/src/renderers/dom/styles.css +61 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +17 -2
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +19 -11
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/series/capability.ts +75 -0
- package/src/series/csv.ts +160 -0
- package/src/series/files.ts +107 -0
- package/src/series/registry.ts +198 -0
- package/src/series/resolve.ts +107 -0
- package/src/series/standalone.ts +48 -0
- package/src/series/types.ts +145 -0
- package/src/series/validate.ts +171 -0
- package/src/sources/gloomberb-cloud/index.ts +7 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +1 -0
- package/src/sources/history-result.ts +1 -0
- package/src/sources/listing-symbols.ts +4 -1
- package/src/sources/provider-router/batches.ts +4 -1
- package/src/sources/provider-router/cache.ts +4 -2
- package/src/sources/provider-router/cached-routes.ts +1 -1
- package/src/sources/provider-router/financial-routes.ts +1 -0
- package/src/sources/provider-router/financials.ts +13 -0
- package/src/sources/provider-router/history.ts +43 -25
- package/src/sources/provider-router/search.ts +1 -0
- package/src/sync/core-contributors.ts +1 -0
- package/src/tickers/company-name.ts +75 -0
- package/src/tickers/instrument-kind.ts +25 -0
- package/src/tickers/open-target.ts +3 -2
- package/src/tickers/search/index.ts +45 -7
- package/src/tickers/search/ranking.ts +33 -17
- package/src/tickers/search/types.ts +7 -1
- package/src/tickers/us-listing.ts +34 -0
- package/src/time-series/hooks.ts +9 -0
- package/src/time-series/resolve.ts +95 -16
- package/src/time-series/types.ts +4 -2
- package/src/time-series/use-chart-resolution.ts +32 -0
- package/src/types/config.ts +6 -0
- package/src/types/data-provider.ts +5 -0
- package/src/types/financials.ts +52 -1
- package/src/types/headless.ts +61 -0
- package/src/types/plugin.ts +19 -0
- package/src/types/price-history.ts +7 -0
- package/src/types/ticker.ts +14 -0
- package/src/ui/host.tsx +18 -0
- package/src/ui/live-resize.ts +81 -0
- package/src/ui/live-stretch.ts +121 -0
- package/src/ui/measured-cells.ts +58 -0
- package/src/ui/pane-fill.ts +11 -0
- package/src/utils/basis-points.ts +17 -0
- package/src/utils/cli-output.ts +7 -1
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/crypto-pair.ts +20 -0
- package/src/utils/currency-pair.ts +19 -0
- package/src/utils/decimal-comma.ts +19 -0
- package/src/utils/dom-screenshot-css.ts +265 -0
- package/src/utils/dom-screenshot-rasters.ts +276 -0
- package/src/utils/dom-screenshot.ts +437 -52
- package/src/utils/exchange-rate-snapshot.ts +6 -2
- package/src/utils/exchanges.ts +107 -1
- package/src/utils/format.ts +16 -3
- package/src/utils/fundamentals.ts +66 -0
- package/src/utils/futures-generic.ts +6 -12
- package/src/utils/fx-market-hours.ts +15 -5
- package/src/utils/price-history.ts +99 -42
- package/src/utils/sec-eps-basis.ts +24 -11
- package/src/utils/sec.ts +61 -7
- package/src/utils/utc-time.ts +51 -4
- package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
- /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
|
@@ -3,6 +3,7 @@ import { scalarPoint, staticSeries } from "../../../components/chart/static/seri
|
|
|
3
3
|
import type { HiringBoard, HiringPayload, HiringSummary } from "../../../api-client/hiring";
|
|
4
4
|
import type { AppAttentionPayload, AppCompany } from "../../../api-client/app-attention";
|
|
5
5
|
import { humanLabel } from "../shared/research-cells";
|
|
6
|
+
import { formatNumber } from "../../../utils/format";
|
|
6
7
|
|
|
7
8
|
export type AttentionKind = "hiring" | "apps";
|
|
8
9
|
export type AttentionTab = "table" | "chart" | "mix" | "peers" | "evidence";
|
|
@@ -28,16 +29,33 @@ export interface AttentionModel {
|
|
|
28
29
|
notices: string[];
|
|
29
30
|
figures: StatItem[];
|
|
30
31
|
sections: Record<AttentionTab, AttentionSection>;
|
|
31
|
-
|
|
32
|
+
/** `since`: the first capture, so a chart still collecting says when it began. */
|
|
33
|
+
chart: { label: string; unit: string; points: Array<{ date: string; value: number | null }>; since?: string | null };
|
|
32
34
|
}
|
|
33
35
|
export const count = (value: number | null | undefined) => value == null ? "--" : value.toLocaleString("en-US", { maximumFractionDigits: 0 });
|
|
36
|
+
/** Decimals per column: percentages and scores to one place, ratings and z-scores to two, counts and remote shares whole. */
|
|
37
|
+
const DECIMALS: Record<string, number> = { score: 1, remote: 0, share: 1, confidence: 0, velocity: 2, rating: 2, drift: 2, z: 2, change: 1 };
|
|
38
|
+
export const SIGNED_COLUMNS = new Set(["net", "change", "velocity", "drift", "growth"]);
|
|
39
|
+
/** A column's number as the pane and the text report print it. */
|
|
40
|
+
export const attentionNumber = (columnId: string, value: number) =>
|
|
41
|
+
`${SIGNED_COLUMNS.has(columnId) && value > 0 ? "+" : ""}${formatNumber(value, DECIMALS[columnId] ?? 0)}`;
|
|
42
|
+
/**
|
|
43
|
+
* A remote share with the roles it is measured on. Most postings never state a
|
|
44
|
+
* work mode, so the share counts only those that do: 100% of 5 is not 100% of
|
|
45
|
+
* a company's 300 roles. A capture that did not count them says "of known".
|
|
46
|
+
*/
|
|
47
|
+
export function remoteText(percentRemote: number | null | undefined, known: number | null | undefined): string | null {
|
|
48
|
+
if (percentRemote == null) return null;
|
|
49
|
+
return `${formatNumber(percentRemote, 0)}% of ${known == null ? "" : `${formatNumber(known, 0)} `}known`;
|
|
50
|
+
}
|
|
34
51
|
const signed = (value: number | null | undefined, decimals = 0) => value == null ? "--" : `${value > 0 ? "+" : ""}${value.toLocaleString("en-US", { maximumFractionDigits: decimals })}`;
|
|
35
52
|
const percent = (value: number | null | undefined) => value == null ? "--" : `${(value * 100).toFixed(1)}%`;
|
|
36
53
|
const column = (id: string, label: string, width: number, numeric = false, flex = false): DataTableColumn => ({ id, label, width, align: numeric ? "right" : "left", ...(flex ? { flexGrow: 1 } : {}) });
|
|
37
54
|
const roleFamily = (value: string | null) => value === "data_ai" || value === "data ai" ? "Data / AI" : value ? humanLabel(value) : null;
|
|
38
|
-
const COMPANY_COLUMNS = [column("symbol", "Ticker", 12), column("name", "Company", 24, false, true), column("open", "Open roles", 12, true), column("net", "Net / week", 12, true), column("remote", "Remote %",
|
|
39
|
-
const HISTORY_COLUMNS = [column("date", "Week", 12), column("open", "Open roles", 12, true), column("added", "Added", 9, true), column("removed", "Removed", 9, true), column("net", "Net", 9, true), column("change", "Change %", 11, true), column("remote", "Remote %",
|
|
40
|
-
|
|
55
|
+
const COMPANY_COLUMNS = [column("symbol", "Ticker", 12), column("name", "Company", 24, false, true), column("open", "Open roles", 12, true), column("net", "Net / week", 12, true), column("remote", "Remote %", 17, true), column("z", "Z-score", 10, true), column("signal", "Signal", 12), column("date", "As of", 12)];
|
|
56
|
+
const HISTORY_COLUMNS = [column("date", "Week", 12), column("open", "Open roles", 12, true), column("added", "Added", 9, true), column("removed", "Removed", 9, true), column("net", "Net", 9, true), column("change", "Change %", 11, true), column("remote", "Remote %", 17, true), column("coverage", "Capture", 13, false, true)];
|
|
57
|
+
/** `remoteKnown` rides along for the remote cell; it is not a column of its own. */
|
|
58
|
+
const companyRow = (row: HiringSummary): AttentionRow => ({ id: row.symbol, symbol: row.symbol, values: { symbol: row.symbol, name: row.name, open: row.latest?.openCount ?? null, net: row.latest?.netChange ?? null, remote: row.latest?.remoteShare == null ? null : row.latest.remoteShare * 100, remoteKnown: row.latest?.remoteKnown ?? null, z: row.zScore, signal: humanLabel(row.signal ?? row.status), date: row.latest?.observedAt.slice(0, 10) ?? null } });
|
|
41
59
|
|
|
42
60
|
export function hiringModel(data: HiringBoard | HiringPayload, mix = "functions"): AttentionModel {
|
|
43
61
|
const detail = "series" in data ? data : null;
|
|
@@ -57,18 +75,19 @@ export function hiringModel(data: HiringBoard | HiringPayload, mix = "functions"
|
|
|
57
75
|
figures: latest ? [
|
|
58
76
|
{ label: "Open roles", value: count(latest.openCount) },
|
|
59
77
|
...(latest.netChange != null ? [{ label: "Net / week", value: signed(latest.netChange), tone: latest.netChange === 0 ? "muted" as const : latest.netChange > 0 ? "positive" as const : "negative" as const }] : []),
|
|
60
|
-
...(latest.remoteShare != null ? [{ label: "Remote", value:
|
|
78
|
+
...(latest.remoteShare != null ? [{ label: "Remote", value: `${formatNumber(latest.remoteShare * 100, 0)}%`,
|
|
79
|
+
detail: latest.remoteKnown == null ? "of known work modes" : `of ${count(latest.remoteKnown)} known` }] : []),
|
|
61
80
|
...(detail?.zScore != null ? [{ label: "Z-score", value: detail.zScore.toFixed(2), detail: detail.signal ? `hiring ${detail.signal}` : undefined,
|
|
62
81
|
tone: detail.signal === "surge" ? "positive" as const : detail.signal === "freeze" ? "negative" as const : undefined }] : []),
|
|
63
82
|
] : [],
|
|
64
83
|
sections: {
|
|
65
84
|
table: { columns: COMPANY_COLUMNS, rows: companies.map(companyRow), empty: "Hiring observations are not available yet." },
|
|
66
|
-
chart: { columns: HISTORY_COLUMNS, rows: [...history].reverse().map((row): AttentionRow => ({ id: row.week, date: row.week, values: { date: row.week, open: row.openCount, added: row.added, removed: row.removed, net: row.netChange, change: row.changePct, remote: row.remoteShare == null ? null : row.remoteShare * 100, coverage: row.completeness }, details: [{ label: "Observed", value: row.observedAt }, { label: "Confidence", value: percent(row.confidence) }, { label: "Capture", value: row.kind }, { label: "Evergreen roles", value: count(row.evergreenCount) }] })), empty: data.preview ? "Full hiring history requires Pro." : "History is accumulating from the first capture." },
|
|
85
|
+
chart: { columns: HISTORY_COLUMNS, rows: [...history].reverse().map((row): AttentionRow => ({ id: row.week, date: row.week, values: { date: row.week, open: row.openCount, added: row.added, removed: row.removed, net: row.netChange, change: row.changePct, remote: row.remoteShare == null ? null : row.remoteShare * 100, remoteKnown: row.remoteKnown, coverage: row.completeness }, details: [{ label: "Observed", value: row.observedAt }, { label: "Confidence", value: percent(row.confidence) }, { label: "Capture", value: row.kind }, { label: "Evergreen roles", value: count(row.evergreenCount) }] })), empty: data.preview ? "Full hiring history requires Pro." : "History is accumulating from the first capture." },
|
|
67
86
|
mix: { columns: [column("label", mixKey === "signals" ? "Signal" : "Group", 28, false, true), column("count", mixKey === "signals" ? "Value" : "Roles", 12, true), column("share", mixKey === "signals" ? "Confidence %" : "Share %", mixKey === "signals" ? 14 : 30, true), ...(mixKey === "signals" ? [column("date", "Observed", 12)] : [])], rows, empty: "No classified observations for this group yet." },
|
|
68
87
|
peers: { columns: COMPANY_COLUMNS, rows: (detail?.peers ?? []).map(companyRow), empty: "No comparable mapped peers yet." },
|
|
69
88
|
evidence: { columns: [column("title", "Role", 32, false, true), column("location", "Location", 24), column("function", "Function", 18), column("seniority", "Seniority", 13), column("date", "Observed", 12), column("confidence", "Confidence %", 13, true)], rows: (detail?.evidence ?? []).map((row): AttentionRow => ({ id: `${row.snapshotId}:${row.id}`, url: row.url ?? row.sourceUrl, values: { title: row.title, location: row.location, function: roleFamily(row.jobFunction), seniority: row.seniority ? humanLabel(row.seniority) : null, date: row.observedAt.slice(0, 10), confidence: row.confidence * 100 }, details: [{ label: "Posting title", value: row.title }, { label: "Location", value: row.location ?? "--" }, { label: "Country", value: row.country ?? "Unknown" }, { label: "Function", value: row.jobFunction ?? "Unclassified" }, { label: "Seniority", value: row.seniority ?? "Unclassified" }, { label: "Remote", value: row.remote === null ? "Unknown" : row.remote ? "Yes" : "No" }, { label: "Observed", value: row.observedAt }, { label: "Revision", value: String(row.revision) }, { label: "Confidence", value: percent(row.confidence) }, { label: "Reporting ticker", value: row.sourceTicker }] })), empty: "No primary posting evidence is available yet." },
|
|
70
89
|
},
|
|
71
|
-
chart: { label: "Open roles", unit: "roles", points: history.map((point) => ({ date: point.week, value: point.openCount })) },
|
|
90
|
+
chart: { label: "Open roles", unit: "roles", points: history.map((point) => ({ date: point.week, value: point.openCount })), since: detail?.coverage.firstObservedAt ?? null },
|
|
72
91
|
};
|
|
73
92
|
if (detail) {
|
|
74
93
|
model.sections.table = { ...model.sections.evidence, columns: model.sections.evidence.columns.filter((entry) => entry.id !== "confidence"), empty: "No current role evidence has been captured yet." };
|
|
@@ -12,20 +12,18 @@ import type { PaneProps } from "../../../types/plugin";
|
|
|
12
12
|
import { Box, ScrollBox, Text, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
|
|
13
13
|
import { SignInWall } from "../cloud/auth-actions";
|
|
14
14
|
import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
|
|
15
|
+
import { useOpenDescription } from "../shared/open-description";
|
|
15
16
|
import { Blurred, LockedOverlay, UpgradeLabel } from "../shared/locked-rows";
|
|
16
17
|
import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
|
|
17
18
|
import { humanLabel, listingCell, missingCell, shareCell, signedColor, toneColor, withoutQuietColumns, type StateTone } from "../shared/research-cells";
|
|
18
19
|
import { AppRankView } from "./app-rank";
|
|
19
20
|
import { useAttentionEvidence } from "./evidence";
|
|
20
21
|
import { cachedAttention, fetchAttention, loadAttention, validateAttention, type AttentionPayload } from "./client";
|
|
21
|
-
import { appsModel, attentionSeries, count, hiringModel, sortAttentionRows, type AttentionKind, type AttentionRow, type AttentionTab } from "./model";
|
|
22
|
+
import { appsModel, attentionNumber, attentionSeries, count, hiringModel, remoteText, SIGNED_COLUMNS, sortAttentionRows, type AttentionKind, type AttentionRow, type AttentionTab } from "./model";
|
|
22
23
|
|
|
23
24
|
type Size = Pick<PaneProps, "width" | "height" | "focused">;
|
|
24
25
|
const rowId = (row: AttentionRow) => row.id;
|
|
25
26
|
const rowDate = (row: AttentionRow) => row.date ? new Date(row.date) : null;
|
|
26
|
-
/** Decimals per column: percentages and scores to one place, ratings and z-scores to two, counts whole. */
|
|
27
|
-
const DECIMALS: Record<string, number> = { score: 1, remote: 1, share: 1, confidence: 0, velocity: 2, rating: 2, drift: 2, z: 2, change: 1 };
|
|
28
|
-
const SIGNED_COLUMNS = new Set(["net", "change", "velocity", "drift", "growth"]);
|
|
29
27
|
const SIGNAL_TONES: Record<string, StateTone> = { Surge: "positive", Freeze: "negative", Stale: "warning", Collecting: "muted", Uncovered: "muted", Ok: "text" };
|
|
30
28
|
/** Columns left out while every row reads the same default: a first capture has no weekly change yet. */
|
|
31
29
|
const QUIET_COLUMNS: Record<string, readonly string[]> = { net: [""], z: [""], change: [""], velocity: [""], drift: [""], growth: [""], added: [""], removed: [""], coverage: ["complete"], spread: [""] };
|
|
@@ -62,6 +60,7 @@ function AttentionView({ kind, symbol, width, height, focused }: Size & { kind:
|
|
|
62
60
|
const desktop = !!useUiCapabilities().nativePaneChrome;
|
|
63
61
|
const host = useRendererHost();
|
|
64
62
|
const { createPaneFromTemplate } = usePluginAppActions();
|
|
63
|
+
const openDescription = useOpenDescription();
|
|
65
64
|
const session = useResearchCloudSession();
|
|
66
65
|
const access = usePlanAccess();
|
|
67
66
|
const accessKey = `${session.requestKey}:${access.hasProAccess ? "full" : "preview"}`;
|
|
@@ -131,7 +130,7 @@ function AttentionView({ kind, symbol, width, height, focused }: Size & { kind:
|
|
|
131
130
|
...(selected && !evidence && selected.details ? [{ id: "evidence", key: "e", label: "vidence", onPress: () => setOpenEvidence(selected.id) }] : []),
|
|
132
131
|
...(selected?.app && selectedSymbol && !symbol ? [{ id: "company", key: "c", label: "ompany", onPress: () => setOpenCompany(selectedSymbol) }] : []),
|
|
133
132
|
...(selectedSymbol ? [
|
|
134
|
-
{ id: "description", key: "d", label: "es", onPress: () =>
|
|
133
|
+
{ id: "description", key: "d", label: "es", onPress: () => openDescription(selectedSymbol) },
|
|
135
134
|
{ id: "financials", key: "f", label: "a", onPress: () => createPaneFromTemplate("financial-analysis-pane", { symbol: selectedSymbol }) },
|
|
136
135
|
{ id: "chart", key: "g", label: "raph", onPress: () => createPaneFromTemplate("chart-composer-pane", { arg: selectedSymbol }) },
|
|
137
136
|
{ id: "related", key: "a", label: kind === "hiring" ? "pps" : " hiring", title: kind === "hiring" ? "App attention" : "Hiring momentum", onPress: () => createPaneFromTemplate(kind === "hiring" ? "apps-pane" : "hiring-pane", { symbol: selectedSymbol }) },
|
|
@@ -139,7 +138,7 @@ function AttentionView({ kind, symbol, width, height, focused }: Size & { kind:
|
|
|
139
138
|
...(model?.preview ? [{ id: "upgrade", key: CLOUD_PLAN_KEY, label: "upgrade", title: "Upgrade to Pro", onPress: upgrade }] : []),
|
|
140
139
|
];
|
|
141
140
|
usePaneStatusFooter({ registrationId: `${kind}:status`, enabled: !openCompany && !app, loading: resource.loading || paged.loadingMore, error: data ? resource.error ?? paged.moreError?.message ?? paged.error?.message : null, stale: resource.data?.stale,
|
|
142
|
-
info: [...(model?.asOf ? [{ id: "observed", parts: [{ text: `as of ${model.asOf.slice(0, 10)}`, tone: "muted" as const }] }] : []), ...(model?.preview ? [{ id: "preview", parts: [{ text: "Pro preview", tone: "warning" as const }] }] : [])], hints });
|
|
141
|
+
info: [...(model?.asOf ? [{ id: "observed", asOf: true, parts: [{ text: `as of ${model.asOf.slice(0, 10)}`, tone: "muted" as const }] }] : []), ...(model?.preview ? [{ id: "preview", parts: [{ text: "Pro preview", tone: "warning" as const }] }] : [])], hints });
|
|
143
142
|
usePaneNoticeFooter({ registrationId: `${kind}:notices`, enabled: !openCompany && !app, focused, notices: [...(model?.notices ?? []), ...(resource.data?.refreshError ? [resource.data.refreshError] : [])] });
|
|
144
143
|
if (!data && isCloudSessionRequired(resource.error)) return <SignInWall placement={kind === "hiring" ? "hiring-momentum-signin" : "app-attention-signin"} action={kind === "hiring" ? "view hiring momentum" : "view app attention"} needsVerification={session.needsVerification} />;
|
|
145
144
|
const countryOptions = data && kind === "apps" ? (data as AppAttentionPayload).coverage.configuredCountries : [];
|
|
@@ -156,7 +155,8 @@ function AttentionView({ kind, symbol, width, height, focused }: Size & { kind:
|
|
|
156
155
|
detailContent={openCompany ? <PaneFooterScope active><AttentionView key={openCompany} kind={kind} symbol={openCompany} width={width} height={bodyHeight - 1} focused={focused} /></PaneFooterScope> : app ? <AppRankView focus={app} accessKey={accessKey} width={width} height={bodyHeight - 1} focused={focused} /> : evidence ? <Evidence row={evidence} width={width} height={bodyHeight - 1} focused={focused} /> : null}
|
|
157
156
|
rootContent={<DataTableView columns={columns} items={rows} rootWidth={width} rootHeight={bodyHeight} focused={focused && !detailOpen} scrollRef={scrollRef} onBodyScrollActivity={scrollMore}
|
|
158
157
|
rootBefore={<ChartTableHeader width={width} height={bodyHeight} tableRows={rows.length} tableColumns={columns} query={query} figures={model.figures}
|
|
159
|
-
chart={tab === "chart" ? { series, ...chartSelection, formatValue: (value) => kind === "hiring" ? count(value) : value.toFixed(1), empty: series.length ? undefined : model.preview ? "Full history requires Pro."
|
|
158
|
+
chart={tab === "chart" ? { series, ...chartSelection, formatValue: (value) => kind === "hiring" ? count(value) : value.toFixed(1), empty: series.length ? undefined : model.preview ? "Full history requires Pro."
|
|
159
|
+
: model.chart.since ? `Collecting history since ${model.chart.since.slice(0, 10)}` : "Collecting history", remoteKind: `${kind}-history` } : null} />}
|
|
160
160
|
selection={{ kind: "id", selectedId: selected?.id ?? null, getId: rowId, onChange: setSelected }} getItemKey={rowId}
|
|
161
161
|
sortColumnId={sort.column} sortDirection={sort.direction} onHeaderClick={(column) => setSort((old) => ({ column, direction: old.column === column && old.direction === "desc" ? "asc" : "desc" }))}
|
|
162
162
|
onActivate={(row) => row.id.startsWith("locked:") ? void upgrade() : row.app ? setOpenApp(row.id) : row.symbol && row.symbol !== symbol && (tab === "peers" || !symbol) ? setOpenCompany(row.symbol) : row.details ? setOpenEvidence(row.id) : row.url ? void host.openExternal(row.url) : undefined}
|
|
@@ -170,8 +170,11 @@ function AttentionView({ kind, symbol, width, height, focused }: Size & { kind:
|
|
|
170
170
|
if (column.id === "symbol" && typeof value === "string") return listingCell(value, colors, state.selected);
|
|
171
171
|
if (column.id === "signal" && typeof value === "string") return { text: value, color: toneColor(SIGNAL_TONES[value] ?? "text", colors), keepColorWhenSelected: value === "Surge" || value === "Freeze" };
|
|
172
172
|
const numeric = typeof value === "number";
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
if (column.id === "remote" && numeric) {
|
|
174
|
+
const known = row.values.remoteKnown;
|
|
175
|
+
return { text: remoteText(value, typeof known === "number" ? known : null) ?? "", value, color: colors.text };
|
|
176
|
+
}
|
|
177
|
+
const text = numeric ? attentionNumber(column.id, value) : LABEL_COLUMNS.has(column.id) ? humanLabel(value) : column.id === "title" ? value.trim() : value;
|
|
175
178
|
if (column.id === "share" && numeric && tab === "mix") return { ...shareCell(text, maxShare ? value / maxShare : null, column.width, colors, state.selected), value };
|
|
176
179
|
return { text, value, color: numeric && SIGNED_COLUMNS.has(column.id) ? signedColor(value, colors)
|
|
177
180
|
: column.id === "name" || column.id === "title" ? colors.textBright : column.id === "date" || column.id === "observed" || column.id === "confidence" ? colors.textDim : colors.text };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AppAttentionPayload } from "../../../api-client/app-attention";
|
|
2
|
+
import type { HiringPayload } from "../../../api-client/hiring";
|
|
3
|
+
import type { SeriesDefinition } from "../../../series/types";
|
|
4
|
+
import { fetchAttention } from "./client";
|
|
5
|
+
|
|
6
|
+
const DAY_MS = 86_400_000;
|
|
7
|
+
|
|
8
|
+
/** The app history window a chart asks for, in the days the server takes (7 to 730). */
|
|
9
|
+
function appHistoryDays(start: Date | undefined): number {
|
|
10
|
+
if (!start) return 730;
|
|
11
|
+
return Math.min(730, Math.max(7, Math.ceil((Date.now() - start.getTime()) / DAY_MS)));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The company signals HIRE and APPS chart: observed open roles each week and
|
|
16
|
+
* the app attention score each day. Pro readings: the free preview carries no
|
|
17
|
+
* history, so they fail with the pane's own words.
|
|
18
|
+
*/
|
|
19
|
+
export const COMPANY_ATTENTION_SERIES: readonly SeriesDefinition[] = [
|
|
20
|
+
{
|
|
21
|
+
id: "hiring:open-roles",
|
|
22
|
+
name: "Observed open roles",
|
|
23
|
+
unit: "jobs",
|
|
24
|
+
frequency: "weekly",
|
|
25
|
+
group: "Alternative data",
|
|
26
|
+
tags: ["hiring", "hiring momentum", "roles", "requisitions", "careers"],
|
|
27
|
+
description: "Requisitions observed on public career boards each week, subsidiaries rolled up through evidenced ownership, as HIRE charts them. Needs Pro.",
|
|
28
|
+
input: "ticker",
|
|
29
|
+
load: async ({ ticker, signal }) => {
|
|
30
|
+
const data = await fetchAttention("hiring", ticker!, {}, undefined, signal) as HiringPayload;
|
|
31
|
+
if (data.preview) throw new Error("Full hiring history requires Pro.");
|
|
32
|
+
if (!data.series.length) throw new Error("History is accumulating from the first capture.");
|
|
33
|
+
return data.series.map((point) => ({ date: point.week, value: point.openCount }));
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "app-attention:score",
|
|
38
|
+
name: "App attention score",
|
|
39
|
+
unit: "score",
|
|
40
|
+
frequency: "daily",
|
|
41
|
+
group: "Alternative data",
|
|
42
|
+
tags: ["apps", "app attention", "mobile", "app ranks", "ratings"],
|
|
43
|
+
description: "A 0 to 100 summary of the public chart ranks of the company's apps across countries, as APPS charts it. Needs Pro.",
|
|
44
|
+
input: "ticker",
|
|
45
|
+
load: async ({ ticker, start, signal }) => {
|
|
46
|
+
const data = await fetchAttention("apps", ticker!, { days: appHistoryDays(start) }, undefined, signal) as AppAttentionPayload;
|
|
47
|
+
if (data.access === "preview") throw new Error("Full app history requires Pro.");
|
|
48
|
+
if (!data.history.length) throw new Error("History is accumulating from the first capture.");
|
|
49
|
+
return data.history.map((point) => ({ date: point.date, value: point.attentionScore }));
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
];
|
|
@@ -13,6 +13,7 @@ import type { PaneProps } from "../../../types/plugin";
|
|
|
13
13
|
import { Box, Text, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
|
|
14
14
|
import { SignInWall } from "../cloud/auth-actions";
|
|
15
15
|
import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
|
|
16
|
+
import { useOpenDescription } from "../shared/open-description";
|
|
16
17
|
import { Blurred, LockedOverlay, UpgradeLabel } from "../shared/locked-rows";
|
|
17
18
|
import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
|
|
18
19
|
import { cachedCompanyData, loadCompanyData, KPI_UNAVAILABLE, validateCompanyData, type CompanyDataset, type CompanyMode } from "./client";
|
|
@@ -77,6 +78,7 @@ function CompanyView({ symbol, exchange, name, mode, width, height, focused }: P
|
|
|
77
78
|
const host = useRendererHost();
|
|
78
79
|
const desktop = !!useUiCapabilities().nativePaneChrome;
|
|
79
80
|
const { createPaneFromTemplate } = usePluginAppActions();
|
|
81
|
+
const openDescription = useOpenDescription();
|
|
80
82
|
const session = useResearchCloudSession();
|
|
81
83
|
const access = usePlanAccess();
|
|
82
84
|
const accessKey = `${session.requestKey}:${access.hasProAccess ? "full" : "preview"}`;
|
|
@@ -165,10 +167,10 @@ function CompanyView({ symbol, exchange, name, mode, width, height, focused }: P
|
|
|
165
167
|
...(preview ? [{ id: "upgrade", key: CLOUD_PLAN_KEY, label: "upgrade", title: "Upgrade to Pro", onPress: openUpgrade }] : []),
|
|
166
168
|
];
|
|
167
169
|
usePaneMenuItems("company-disclosures:research", () => symbol ? [
|
|
168
|
-
{ id: "description", label: "Description (DES)", onSelect: () =>
|
|
170
|
+
{ id: "description", label: "Description (DES)", onSelect: () => openDescription(symbol) },
|
|
169
171
|
{ id: "financials", label: "Financial Analysis (FA)", onSelect: () => createPaneFromTemplate("financial-analysis-pane", { symbol }) },
|
|
170
172
|
{ id: "price-chart", label: "Price Chart (G)", onSelect: () => createPaneFromTemplate("chart-composer-pane", { arg: symbol }) },
|
|
171
|
-
] : null, [symbol, createPaneFromTemplate]);
|
|
173
|
+
] : null, [symbol, createPaneFromTemplate, openDescription]);
|
|
172
174
|
usePaneStatusFooter({ registrationId: "company-kpis", loading: resource.loading, error: data ? resource.error : null, stale: resource.data?.stale,
|
|
173
175
|
info: data?.asOf ? [{ id: "as-of", parts: [{ text: `as of ${data.asOf.slice(0, 10)}`, tone: "muted" as const }] }] : [], hints });
|
|
174
176
|
usePaneNoticeFooter({ registrationId: "company-kpis:notices", focused, notices: [resource.data?.refreshError,
|
|
@@ -32,6 +32,7 @@ import { cotModule } from "./cot";
|
|
|
32
32
|
import { doeModule } from "./doe";
|
|
33
33
|
import { gpuModule } from "./gpu";
|
|
34
34
|
import { fxMatrixModule } from "./fx-matrix";
|
|
35
|
+
import { docsModule } from "./docs";
|
|
35
36
|
import { helpModule } from "./help";
|
|
36
37
|
import { positionSizerModule } from "./kelly-sizer";
|
|
37
38
|
import { layoutManagerModule } from "./layout-manager";
|
|
@@ -57,6 +58,7 @@ import { socialMentionsModule } from "./social-mentions";
|
|
|
57
58
|
import { timeSalesModule } from "./time-sales";
|
|
58
59
|
import { estimateRevisionsModule } from "./estimate-revisions";
|
|
59
60
|
import { chartComposerModule } from "./chart-composer";
|
|
61
|
+
import { userSeriesModule } from "./chart-composer/user-series";
|
|
60
62
|
import { congressResearchModule } from "./congress-trades";
|
|
61
63
|
import { dividendYieldModule } from "./dividend-yield";
|
|
62
64
|
import { executivesModule } from "./executives";
|
|
@@ -104,7 +106,7 @@ import {
|
|
|
104
106
|
|
|
105
107
|
export const applicationPlugin = composeBuiltinPlugin({
|
|
106
108
|
...applicationPluginMeta,
|
|
107
|
-
modules: [layoutManagerModule, pluginMarketplaceModule, helpModule, changelogModule, connectionsModule],
|
|
109
|
+
modules: [layoutManagerModule, pluginMarketplaceModule, helpModule, changelogModule, connectionsModule, docsModule],
|
|
108
110
|
});
|
|
109
111
|
|
|
110
112
|
export const portfolioPlugin = composeBuiltinPlugin({
|
|
@@ -121,18 +123,22 @@ const tickerResearchModule = (module: PluginModule) => ({ module, stateId: "tick
|
|
|
121
123
|
export const tickerCorePlugin = composeBuiltinPlugin({
|
|
122
124
|
...tickerCorePluginMeta,
|
|
123
125
|
modules: [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
...[
|
|
127
|
+
tickerDetailModule,
|
|
128
|
+
chartComposerModule,
|
|
129
|
+
researchModule,
|
|
130
|
+
estimateRevisionsModule,
|
|
131
|
+
companyKpisModule,
|
|
132
|
+
revenueBreakdownModule,
|
|
133
|
+
dividendYieldModule,
|
|
134
|
+
reverseDcfModule,
|
|
135
|
+
peBandModule,
|
|
136
|
+
executivesModule,
|
|
137
|
+
timeSalesModule,
|
|
138
|
+
].map(rendererOnly),
|
|
139
|
+
// Reads the profile's series folder, so it runs where the disk is: the desktop backend too.
|
|
140
|
+
userSeriesModule,
|
|
141
|
+
],
|
|
136
142
|
});
|
|
137
143
|
|
|
138
144
|
export const optionsVolatilityPlugin = composeBuiltinPlugin({
|
|
@@ -3,14 +3,16 @@ import { cotBoardCache } from "./client";
|
|
|
3
3
|
import { cotHeadless } from "./headless";
|
|
4
4
|
import { COT_ROOT_OPTIONS, cotContractCode, cotContractCodeForTicker, cotRoot, cotUnknownMarketMessage } from "./model";
|
|
5
5
|
import { CotPane } from "./pane";
|
|
6
|
+
import { COT_SERIES } from "./series";
|
|
6
7
|
|
|
7
8
|
export const cotModule: PluginModule = {
|
|
9
|
+
series: COT_SERIES,
|
|
8
10
|
panes: [{ id: "cot", name: "CFTC Positioning", icon: "P", component: CotPane,
|
|
9
11
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 106, height: 30 },
|
|
10
12
|
tableExport: true, headless: cotHeadless }],
|
|
11
13
|
paneTemplates: [{ id: "cot-cot", paneId: "cot", label: "CFTC Positioning",
|
|
12
14
|
description: "Weekly futures positioning, changes, historical percentiles and cross-market extremes.",
|
|
13
|
-
keywords: ["cot", "cftc", "positioning", "commitments", "managed money", "net spec"],
|
|
15
|
+
keywords: ["cot", "cftc", "positioning", "commitments", "managed money", "net spec", "crude oil", "natural gas"],
|
|
14
16
|
shortcut: { prefix: "COT", aliases: ["CFTC"], argKind: "text" as const, argPlaceholder: "code or root", argOptional: true,
|
|
15
17
|
argOptions: () => COT_ROOT_OPTIONS }, headless: cotHeadless,
|
|
16
18
|
createInstance: (context, options) => {
|
|
@@ -5,6 +5,7 @@ import type { PricePoint } from "../../../types/financials";
|
|
|
5
5
|
import { formatPriceObservation } from "../../../market-data/market/format";
|
|
6
6
|
import { cleanFloat32Price } from "../../../cli/history-rows";
|
|
7
7
|
import { cryptoPairCoin, isFuturesSymbol } from "../shared/crypto-pair";
|
|
8
|
+
import { FUTURES_CONTRACTS, futuresBoardContract, futuresBoardContractForTicker, type FuturesContract } from "../../../market-data/futures-board";
|
|
8
9
|
|
|
9
10
|
export const COT_CLASSES: Record<CotFamily, Array<{ value: CotClass; label: string }>> = {
|
|
10
11
|
legacy: [{ value: "noncommercial", label: "Noncommercial" }, { value: "commercial", label: "Commercial" }, { value: "nonreportable", label: "Nonreportable" }],
|
|
@@ -29,7 +30,7 @@ const ROOTS: Record<string, { code: string; exchange: string; priceSymbol: strin
|
|
|
29
30
|
SOL: { code: "177741", exchange: "CME", priceSymbol: null, name: "Solana", crypto: true },
|
|
30
31
|
XRP: { code: "176740", exchange: "CME", priceSymbol: null, name: "XRP", crypto: true },
|
|
31
32
|
};
|
|
32
|
-
/** The
|
|
33
|
+
/** The verified root aliases, labeled "ZQ 30-Day Fed Funds"; `cotContractCode` also takes every FUT board root the CFTC reports. */
|
|
33
34
|
export const COT_ROOT_OPTIONS = Object.entries(ROOTS).map(([root, row]) => ({ value: root, label: root === row.name ? root : `${root} ${row.name}` }));
|
|
34
35
|
/** The crypto roots named for a coin, which a coin pair (BTC-USD) can stand for. The micros have no pair. */
|
|
35
36
|
const COIN_PAIR_ROOTS: ReadonlySet<string> = new Set(["BTC", "ETH", "SOL", "XRP"]);
|
|
@@ -37,9 +38,12 @@ const COIN_PAIR_ROOTS: ReadonlySet<string> = new Set(["BTC", "ETH", "SOL", "XRP"
|
|
|
37
38
|
* Front-month price overlays for major markets without a root alias, each
|
|
38
39
|
* checked to return five years of daily history (2026-09-24). Keyed by CFTC
|
|
39
40
|
* code, never added to ROOTS: an alias would also claim the equity ticker under
|
|
40
|
-
* the cursor (ZM, ZS, PL).
|
|
41
|
-
*
|
|
42
|
-
*
|
|
41
|
+
* the cursor (ZM, ZS, PL). A FUT board root typed as an argument finds its
|
|
42
|
+
* market here through its price symbol (NG is NG=F, so 023651). No cash index
|
|
43
|
+
* stands in, so the dollar index has none. Orange juice and lumber were added
|
|
44
|
+
* on 2026-10-11 so their board roots open a market; lumber's contract (LBR)
|
|
45
|
+
* started in August 2022, so its price begins there. Bitcoin and ether are in
|
|
46
|
+
* ROOTS, where `cotContractCodeForTicker` keeps the same guard for their aliases.
|
|
43
47
|
*/
|
|
44
48
|
const PRICE_OVERLAYS: Record<string, { exchange: string; priceSymbol: string }> = {
|
|
45
49
|
"209742": { exchange: "CME", priceSymbol: "NQ=F" }, "239742": { exchange: "CME", priceSymbol: "RTY=F" },
|
|
@@ -60,9 +64,16 @@ const PRICE_OVERLAYS: Record<string, { exchange: string; priceSymbol: string }>
|
|
|
60
64
|
"007601": { exchange: "CBT", priceSymbol: "ZL=F" }, "026603": { exchange: "CBT", priceSymbol: "ZM=F" },
|
|
61
65
|
"083731": { exchange: "NYB", priceSymbol: "KC=F" }, "080732": { exchange: "NYB", priceSymbol: "SB=F" },
|
|
62
66
|
"033661": { exchange: "NYB", priceSymbol: "CT=F" }, "073732": { exchange: "NYB", priceSymbol: "CC=F" },
|
|
67
|
+
"040701": { exchange: "NYB", priceSymbol: "OJ=F" }, "058644": { exchange: "CME", priceSymbol: "LBR=F" },
|
|
63
68
|
"057642": { exchange: "CME", priceSymbol: "LE=F" }, "054642": { exchange: "CME", priceSymbol: "HE=F" },
|
|
64
69
|
"061641": { exchange: "CME", priceSymbol: "GF=F" },
|
|
65
70
|
};
|
|
71
|
+
/** The CFTC market a FUT board root reports under, by its price symbol; null for one the CFTC does not report (TTF). */
|
|
72
|
+
function boardMarketCode(contract: FuturesContract): string | null {
|
|
73
|
+
return Object.values(ROOTS).find((row) => row.priceSymbol === contract.symbol)?.code
|
|
74
|
+
?? Object.entries(PRICE_OVERLAYS).find(([, row]) => row.priceSymbol === contract.symbol)?.[0]
|
|
75
|
+
?? null;
|
|
76
|
+
}
|
|
66
77
|
/**
|
|
67
78
|
* The markets most readers mean by "positioning", verified against the live
|
|
68
79
|
* CFTC boards on 2026-09-22. A code missing from a report family simply does
|
|
@@ -90,14 +101,26 @@ export const COT_SCOPES = [{ value: "major", label: "Major markets" }, { value:
|
|
|
90
101
|
export type CotScope = typeof COT_SCOPES[number]["value"];
|
|
91
102
|
export function cotScope(value: unknown): CotScope { return value === "all" ? "all" : "major"; }
|
|
92
103
|
|
|
93
|
-
|
|
104
|
+
const rootAlias = (input: string) => ROOTS[input === "VIX" ? "VX" : input]?.code ?? null;
|
|
105
|
+
const marketCode = (input: string) => /^[0-9A-Z]{5}[0-9A-Z+]$/.test(input) ? input : null;
|
|
106
|
+
/**
|
|
107
|
+
* A CFTC code or root typed as an argument or stored in a pane: every alias,
|
|
108
|
+
* crypto included, and every FUT board root the CFTC reports. A typed root is
|
|
109
|
+
* always the future, so ZM here is soybean meal, never Zoom.
|
|
110
|
+
*/
|
|
94
111
|
export function cotContractCode(value: unknown): string | null {
|
|
95
112
|
if (typeof value !== "string") return null;
|
|
96
113
|
const input = value.trim().toUpperCase().replace(/=F$/, "");
|
|
97
|
-
|
|
114
|
+
const board = futuresBoardContract(value);
|
|
115
|
+
return rootAlias(input) ?? (board ? boardMarketCode(board) : null) ?? marketCode(input);
|
|
98
116
|
}
|
|
99
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* What a market `cotContractCode` does not know is told: a FUT board root the
|
|
119
|
+
* CFTC does not report says so; anything else gets roots and a code that work.
|
|
120
|
+
*/
|
|
100
121
|
export function cotUnknownMarketMessage(value: unknown): string {
|
|
122
|
+
const board = futuresBoardContract(value);
|
|
123
|
+
if (board) return `${board.code} (${board.name}) is not reported in the CFTC COT.`;
|
|
101
124
|
const roots = ["ES", "CL", "GC", "ZN"].filter((root) => ROOTS[root]);
|
|
102
125
|
return `Unknown market "${String(value)}". Try ${roots.join(", ")} or a CFTC market code such as ${ROOTS.GC!.code}.`;
|
|
103
126
|
}
|
|
@@ -106,18 +129,24 @@ export function cotUnknownMarketMessage(value: unknown): string {
|
|
|
106
129
|
* argument. The crypto aliases are also US ticker symbols (Grayscale's mini
|
|
107
130
|
* trusts are BTC and ETH, MetLife is MET), so a bare one is the equity's and
|
|
108
131
|
* never claims the market: only a futures symbol (BTC=F) or a coin pair
|
|
109
|
-
* (BTC-USD) does.
|
|
132
|
+
* (BTC-USD) does. A FUT board root claims it the same way: as its futures
|
|
133
|
+
* symbol (NG=F) or a code no security uses (ZW), never a bare code a security
|
|
134
|
+
* keeps (ZM is Zoom).
|
|
110
135
|
*/
|
|
111
136
|
export function cotContractCodeForTicker(ticker: unknown): string | null {
|
|
112
137
|
if (typeof ticker !== "string") return null;
|
|
113
138
|
const coin = cryptoPairCoin(ticker);
|
|
114
139
|
if (coin) return COIN_PAIR_ROOTS.has(coin) ? ROOTS[coin]!.code : null;
|
|
115
140
|
const input = ticker.trim().toUpperCase().replace(/=F$/, "");
|
|
116
|
-
|
|
141
|
+
if (ROOTS[input]?.crypto && !isFuturesSymbol(ticker)) return null;
|
|
142
|
+
const board = futuresBoardContractForTicker(ticker);
|
|
143
|
+
return rootAlias(input) ?? (board ? boardMarketCode(board) : null) ?? marketCode(input);
|
|
117
144
|
}
|
|
118
|
-
/** The
|
|
145
|
+
/** The futures root a CFTC code belongs to, for titles readers recognize: a verified alias, else its FUT board root. */
|
|
119
146
|
export function cotRoot(code: string): string | null {
|
|
120
|
-
return Object.entries(ROOTS).find(([, row]) => row.code === code)?.[0]
|
|
147
|
+
return Object.entries(ROOTS).find(([, row]) => row.code === code)?.[0]
|
|
148
|
+
?? FUTURES_CONTRACTS.find((contract) => boardMarketCode(contract) === code)?.code
|
|
149
|
+
?? null;
|
|
121
150
|
}
|
|
122
151
|
export function cotPriceMapping(code: string) { return Object.values(ROOTS).find((row) => row.code === code) ?? PRICE_OVERLAYS[code] ?? null; }
|
|
123
152
|
export function cotClass(family: CotFamily, value: unknown): CotClass {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SeriesDefinition } from "../../../series/types";
|
|
2
|
+
import { fetchCotContract } from "./client";
|
|
3
|
+
import { COT_ROOT_OPTIONS, cotContractCode, cotNetPoints } from "./model";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Speculative (noncommercial) net positioning for each market the COT pane
|
|
7
|
+
* names by root, from the legacy report every market is in. The pane's other
|
|
8
|
+
* trader classes and markets stay in the pane.
|
|
9
|
+
*/
|
|
10
|
+
export const COT_SERIES: readonly SeriesDefinition[] = COT_ROOT_OPTIONS.flatMap((option): SeriesDefinition[] => {
|
|
11
|
+
const code = cotContractCode(option.value);
|
|
12
|
+
if (!code) return [];
|
|
13
|
+
const market = option.label.startsWith(`${option.value} `) ? option.label.slice(option.value.length + 1) : option.label;
|
|
14
|
+
return [{
|
|
15
|
+
id: `cot:${option.value.toLowerCase()}-noncommercial-net`,
|
|
16
|
+
name: `${market}: noncommercial net position`,
|
|
17
|
+
unit: "contracts",
|
|
18
|
+
frequency: "weekly",
|
|
19
|
+
group: "Positioning",
|
|
20
|
+
tags: ["COT", "commitments of traders", "speculators", "net spec", option.value],
|
|
21
|
+
publisher: "CFTC",
|
|
22
|
+
load: async () => {
|
|
23
|
+
const payload = await fetchCotContract(code, "legacy");
|
|
24
|
+
return { points: cotNetPoints(payload.history, "noncommercial").map((point) => ({ date: point.date, value: point.value })) };
|
|
25
|
+
},
|
|
26
|
+
}];
|
|
27
|
+
});
|
|
@@ -5,6 +5,7 @@ import { cdxBoardCache, sovrBoardCache } from "./client";
|
|
|
5
5
|
import { cdxHeadless, sovrHeadless } from "./headless";
|
|
6
6
|
import { CDX_PANE_ID } from "./model";
|
|
7
7
|
import { CreditBoardsPane } from "./pane";
|
|
8
|
+
import { CREDIT_BOARD_SERIES } from "./series";
|
|
8
9
|
|
|
9
10
|
/** One board. A later command retargets this instance; a fresh id would open a second window. */
|
|
10
11
|
function openCreditBoard(context: PaneTemplateContext, tab: "index" | "sovereign"): PaneTemplateInstanceConfig {
|
|
@@ -16,6 +17,7 @@ function openCreditBoard(context: PaneTemplateContext, tab: "index" | "sovereign
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export const creditBoardsModule: PluginModule = {
|
|
20
|
+
series: CREDIT_BOARD_SERIES,
|
|
19
21
|
panes: [
|
|
20
22
|
{
|
|
21
23
|
id: CDX_PANE_ID,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CreditBoardPoint } from "../../../api-client/credit-boards";
|
|
2
|
+
import type { SeriesDefinition, SeriesLoadResult } from "../../../series/types";
|
|
3
|
+
import { cachedSeriesWarning } from "../shared/series-warning";
|
|
4
|
+
import { loadCdxBoard, loadSovrBoard } from "./client";
|
|
5
|
+
|
|
6
|
+
/** The five on-the-run 5Y index contracts the CDX tab lists; IG and iTraxx quote spread, HY and EM price. */
|
|
7
|
+
const INDEXES = [
|
|
8
|
+
{ code: "IG", name: "CDX IG 5Y", quote: "spread" },
|
|
9
|
+
{ code: "HY", name: "CDX HY 5Y", quote: "price" },
|
|
10
|
+
{ code: "EM", name: "CDX EM 5Y", quote: "price" },
|
|
11
|
+
{ code: "MAIN", name: "iTraxx Main 5Y", quote: "spread" },
|
|
12
|
+
{ code: "XOVER", name: "iTraxx Crossover 5Y", quote: "spread" },
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
/** The sovereigns the SOVR tab follows, by ISO code. */
|
|
16
|
+
const SOVEREIGNS: ReadonlyArray<readonly [code: string, name: string]> = [
|
|
17
|
+
["BR", "Brazil"], ["MX", "Mexico"], ["CO", "Colombia"], ["TR", "Turkey"], ["ZA", "South Africa"],
|
|
18
|
+
["CN", "China"], ["ID", "Indonesia"], ["CL", "Chile"], ["PE", "Peru"], ["PH", "Philippines"],
|
|
19
|
+
["AR", "Argentina"], ["PA", "Panama"], ["SA", "Saudi Arabia"], ["MY", "Malaysia"], ["EG", "Egypt"],
|
|
20
|
+
["KR", "Korea"], ["IT", "Italy"], ["FR", "France"], ["ES", "Spain"], ["GR", "Greece"],
|
|
21
|
+
["IE", "Ireland"], ["JP", "Japan"], ["IL", "Israel"], ["PL", "Poland"], ["QA", "Qatar"],
|
|
22
|
+
["AD", "Abu Dhabi"], ["BH", "Bahrain"], ["OM", "Oman"], ["MA", "Morocco"], ["NG", "Nigeria"],
|
|
23
|
+
["DO", "Dominican Republic"], ["CI", "Ivory Coast"],
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function boardPoints(
|
|
27
|
+
rows: ReadonlyArray<{ id: string; points: readonly CreditBoardPoint[] }>,
|
|
28
|
+
id: string,
|
|
29
|
+
label: string,
|
|
30
|
+
resource: { stale?: boolean; refreshError?: string | null },
|
|
31
|
+
): SeriesLoadResult {
|
|
32
|
+
const row = rows.find((entry) => entry.id === id);
|
|
33
|
+
if (!row) throw new Error(`${label} is not on the board right now.`);
|
|
34
|
+
const warning = cachedSeriesWarning(resource);
|
|
35
|
+
return {
|
|
36
|
+
points: row.points.map((point) => ({ date: point.date, value: point.level })),
|
|
37
|
+
...(warning ? { warning } : {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const CREDIT_BOARD_SERIES: readonly SeriesDefinition[] = [
|
|
42
|
+
...INDEXES.map((index): SeriesDefinition => ({
|
|
43
|
+
id: `cdx:${index.code.toLowerCase()}`,
|
|
44
|
+
name: index.name,
|
|
45
|
+
unit: index.quote === "spread" ? "bp" : "points",
|
|
46
|
+
frequency: "daily",
|
|
47
|
+
group: "Credit",
|
|
48
|
+
tags: ["CDS", "index CDS", index.quote, "credit default swap"],
|
|
49
|
+
load: async () => {
|
|
50
|
+
const resource = await loadCdxBoard();
|
|
51
|
+
return boardPoints(resource.payload.indexes, index.code, index.name, resource);
|
|
52
|
+
},
|
|
53
|
+
})),
|
|
54
|
+
...SOVEREIGNS.map(([code, name]): SeriesDefinition => ({
|
|
55
|
+
id: `sovereign-cds:${code.toLowerCase()}`,
|
|
56
|
+
name: `${name} 5Y sovereign CDS`,
|
|
57
|
+
unit: "bp",
|
|
58
|
+
frequency: "daily",
|
|
59
|
+
group: "Credit",
|
|
60
|
+
tags: ["CDS", "sovereign", code, "credit default swap"],
|
|
61
|
+
load: async () => {
|
|
62
|
+
const resource = await loadSovrBoard();
|
|
63
|
+
return boardPoints(resource.payload.sovereigns, code, name, resource);
|
|
64
|
+
},
|
|
65
|
+
})),
|
|
66
|
+
];
|
|
@@ -20,6 +20,7 @@ import type { PaneProps } from "../../../types/plugin";
|
|
|
20
20
|
import { formatPercentileRank } from "../../../utils/format";
|
|
21
21
|
import { usePluginPaneState } from "../../runtime";
|
|
22
22
|
import type { PluginModule } from "../plugin-module";
|
|
23
|
+
import { CREDIT_SPREAD_SERIES } from "./series";
|
|
23
24
|
import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
24
25
|
import { getCachedCreditConditions, loadCreditConditions } from "./client";
|
|
25
26
|
import { creditConditionsHeadless } from "./headless";
|
|
@@ -137,6 +138,7 @@ export function CreditConditionsPane({ paneId, focused, width, height }: PanePro
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
export const creditConditionsModule: PluginModule = {
|
|
141
|
+
series: CREDIT_SPREAD_SERIES,
|
|
140
142
|
panes: [{
|
|
141
143
|
id: "credit-conditions",
|
|
142
144
|
name: "Credit Spreads",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { fredSeriesUrl } from "../../../sources/gloomberb-cloud/fred-series";
|
|
2
|
+
import type { SeriesDefinition } from "../../../series/types";
|
|
3
|
+
|
|
4
|
+
/** Corporate bond option-adjusted spreads by rating, and the effective yields of both indexes. */
|
|
5
|
+
const ROWS: ReadonlyArray<readonly [seriesId: string, name: string, tags: readonly string[]]> = [
|
|
6
|
+
["BAMLC0A0CM", "US investment grade corporate OAS", ["spread", "IG", "investment grade"]],
|
|
7
|
+
["BAMLC0A1CAAA", "AAA corporate OAS", ["spread", "AAA", "investment grade"]],
|
|
8
|
+
["BAMLC0A2CAA", "AA corporate OAS", ["spread", "AA", "investment grade"]],
|
|
9
|
+
["BAMLC0A3CA", "A corporate OAS", ["spread", "single A", "investment grade"]],
|
|
10
|
+
["BAMLC0A4CBBB", "BBB corporate OAS", ["spread", "BBB", "investment grade"]],
|
|
11
|
+
["BAMLH0A0HYM2", "US high yield OAS", ["spread", "HY", "high yield", "junk"]],
|
|
12
|
+
["BAMLC0A0CMEY", "US investment grade corporate effective yield", ["yield", "IG", "investment grade"]],
|
|
13
|
+
["BAMLH0A0HYM2EY", "US high yield effective yield", ["yield", "HY", "high yield", "junk"]],
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export const CREDIT_SPREAD_SERIES: readonly SeriesDefinition[] = ROWS.map(([seriesId, name, tags]) => ({
|
|
17
|
+
id: `fred:${seriesId}`,
|
|
18
|
+
name,
|
|
19
|
+
unit: "%",
|
|
20
|
+
frequency: "daily",
|
|
21
|
+
group: "Credit",
|
|
22
|
+
tags: [...tags, "corporate bonds"],
|
|
23
|
+
publisher: "FRED",
|
|
24
|
+
url: fredSeriesUrl(seriesId),
|
|
25
|
+
expression: `FRED:${seriesId}`,
|
|
26
|
+
}));
|