@gloom-sh/terminal 0.18.5 → 0.19.1
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +8 -21
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +10 -10
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +33 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +22 -4
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -8,14 +8,14 @@ import { Box, Text, useUiCapabilities } from "../../../ui";
|
|
|
8
8
|
import { wrapTextLines } from "../../../utils/text-wrap";
|
|
9
9
|
import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
10
10
|
import { compareSortValues, nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
|
|
11
|
-
import { loadIvScreen, loadScreenVolatilities } from "./client";
|
|
12
|
-
import { formatPoints, formatRank, formatSpot, formatVol, readingLabel, sharedDates, shortDate,
|
|
13
|
-
import { projectRichCheap, type RichCheapRow, richCheapDates, screenPaneNotices, type VcaPreset } from "./model";
|
|
11
|
+
import { loadIvScreen, loadScreenVolatilities, type ScreenHv } from "./client";
|
|
12
|
+
import { formatPoints, formatRank, formatSpot, formatVol, readingLabel, sharedDates, shortDate, vcaDefinitions, verdictLabel } from "./format";
|
|
13
|
+
import { projectRichCheap, readingSource, type RichCheapRow, richCheapDates, screenPaneNotices, type VcaPreset } from "./model";
|
|
14
14
|
import { vcaUniverse } from "./universe";
|
|
15
15
|
import { MovedTickerState } from "../../../components/layout/pane/ticker-fate-gate";
|
|
16
16
|
import type { MovedTickerChange } from "../../../tickers/ticker-changes";
|
|
17
17
|
|
|
18
|
-
type SortId = keyof RichCheapRow
|
|
18
|
+
type SortId = Exclude<keyof RichCheapRow, "stale">;
|
|
19
19
|
const HV_WINDOW = 20;
|
|
20
20
|
/** Pane borders and the footer's own padding. */
|
|
21
21
|
const FOOTER_MARGIN = 6;
|
|
@@ -31,9 +31,10 @@ const COLUMNS: DataTableColumn[] = [
|
|
|
31
31
|
{ id: "iv1y", label: "IV1Y", width: 7, align: "right" },
|
|
32
32
|
{ id: "iv1yPercentile", label: "IVP1Y", width: 6, align: "right" },
|
|
33
33
|
{ id: "termSlope", label: "30-90", width: 7, align: "right" },
|
|
34
|
-
{ id: "
|
|
35
|
-
{ id: "skewDate", label: "
|
|
34
|
+
{ id: "riskReversal", label: "25D RR", width: 9, align: "right" },
|
|
35
|
+
{ id: "skewDate", label: "RR as of", width: 11, align: "right" },
|
|
36
36
|
{ id: "hv", label: `HV${HV_WINDOW}`, width: 7, align: "right" },
|
|
37
|
+
{ id: "hvDate", label: "HV as of", width: 9, align: "right" },
|
|
37
38
|
{ id: "ivHv", label: "IV/HV", width: 6, align: "right" },
|
|
38
39
|
];
|
|
39
40
|
|
|
@@ -49,7 +50,7 @@ export function IvScreenPane({ paneId, width, height, focused }: PaneProps) {
|
|
|
49
50
|
const universe = useMemo(() => vcaUniverse(scope, symbolsText, collectionId, [...tickers.values()]), [scope, symbolsText, collectionId, tickers]);
|
|
50
51
|
const key = universe.instruments.map((instrument) => instrument.symbol).join(",");
|
|
51
52
|
const controller = useRef<AbortController | null>(null);
|
|
52
|
-
const [hv, setHv] = useState<Map<string,
|
|
53
|
+
const [hv, setHv] = useState<Map<string, ScreenHv | null>>(new Map());
|
|
53
54
|
// Symbols whose company moved: their rows have no HV and the footer says where each company went.
|
|
54
55
|
const [moved, setMoved] = useState<Array<{ symbol: string; change: MovedTickerChange }>>([]);
|
|
55
56
|
const loader = useCallback(async () => {
|
|
@@ -83,17 +84,21 @@ export function IvScreenPane({ paneId, width, height, focused }: PaneProps) {
|
|
|
83
84
|
}, [resource.data, hv, sort]);
|
|
84
85
|
const dates = useMemo(() => richCheapDates(rows), [rows]);
|
|
85
86
|
// A date every row shares moves to the footer; a column no row fills, or whose dates agree, is left out.
|
|
87
|
+
const hasSkew = useMemo(() => rows.some((row) => row.riskReversal != null), [rows]);
|
|
88
|
+
// A close solved from quote midpoints names itself, which a date alone has no room for.
|
|
89
|
+
const dateWidth = useMemo(() => rows.some((row) => !row.stale && readingSource(row.method, row.basis) === "close mid") ? 15 : 11, [rows]);
|
|
86
90
|
const columns = useMemo(() => COLUMNS.filter((column) => {
|
|
87
91
|
switch (column.id) {
|
|
88
92
|
case "date": return !dates.reading;
|
|
89
93
|
case "rankDate": return dates.rankApart;
|
|
90
94
|
case "iv1y": return rows.some((row) => row.iv1y != null);
|
|
91
95
|
case "iv1yPercentile": return rows.some((row) => row.iv1yPercentile != null);
|
|
92
|
-
case "
|
|
96
|
+
case "riskReversal": return hasSkew;
|
|
93
97
|
case "skewDate": return dates.skewApart;
|
|
98
|
+
case "hvDate": return dates.hvApart;
|
|
94
99
|
default: return true;
|
|
95
100
|
}
|
|
96
|
-
}), [rows, dates]);
|
|
101
|
+
}).map((column) => column.id === "date" ? { ...column, width: dateWidth } : column), [rows, dates, hasSkew, dateWidth]);
|
|
97
102
|
const [selected, setSelected] = useState<string | null>(null);
|
|
98
103
|
const coverageNotices = useMemo(() => screenPaneNotices(rows), [rows]);
|
|
99
104
|
// One symbol to screen whose company moved has nothing to show but where it went, as a ticker's panes say.
|
|
@@ -117,10 +122,10 @@ export function IvScreenPane({ paneId, width, height, focused }: PaneProps) {
|
|
|
117
122
|
const cell = (row: RichCheapRow, id: string): { text: string; color?: string } => {
|
|
118
123
|
switch (id) {
|
|
119
124
|
case "symbol": return { text: row.symbol, color: colors.textBright };
|
|
120
|
-
case "iv30": return { text: formatVol(row.iv30), color: colors.warning };
|
|
125
|
+
case "iv30": return { text: formatVol(row.iv30), color: row.stale ? colors.textDim : colors.warning };
|
|
121
126
|
case "spot": return { text: formatSpot(row.spot), color: colors.textDim };
|
|
122
|
-
case "date": return { text: row.date ? readingLabel(row.date, row.method, true)
|
|
123
|
-
: row.status === "queued" || row.status === "backfilling" ? row.status : "--", color: colors.textDim };
|
|
127
|
+
case "date": return { text: row.date ? readingLabel(row.date, row.method, true, { basis: row.basis, stale: row.stale })
|
|
128
|
+
: row.status === "queued" || row.status === "backfilling" ? row.status : "--", color: row.stale ? colors.warning : colors.textDim };
|
|
124
129
|
case "rankDate": return { text: row.rankDate ? `${shortDate(row.rankDate)} close` : "--", color: colors.textDim };
|
|
125
130
|
case "rank": return { text: formatRank(row.rank) };
|
|
126
131
|
case "percentile": return { text: formatRank(row.percentile) };
|
|
@@ -128,16 +133,18 @@ export function IvScreenPane({ paneId, width, height, focused }: PaneProps) {
|
|
|
128
133
|
case "iv1y": return { text: formatVol(row.iv1y) };
|
|
129
134
|
case "iv1yPercentile": return { text: formatRank(row.iv1yPercentile) };
|
|
130
135
|
case "termSlope": return { text: formatPoints(row.termSlope), color: row.termSlope != null && row.termSlope > 0 ? colors.negative : colors.text };
|
|
131
|
-
case "
|
|
136
|
+
case "riskReversal": return { text: formatPoints(row.riskReversal) };
|
|
132
137
|
case "skewDate": return { text: row.skewDate ? readingLabel(row.skewDate, "quote-mid", true) : "--", color: colors.textDim };
|
|
133
138
|
case "hv": return { text: hv.has(row.symbol) ? formatVol(row.hv) : "...", color: colors.positive };
|
|
139
|
+
case "hvDate": return { text: row.hvDate ? shortDate(row.hvDate) : "--", color: colors.textDim };
|
|
134
140
|
case "ivHv": return { text: row.ivHv == null ? "--" : row.ivHv.toFixed(2) };
|
|
135
141
|
default: return { text: "" };
|
|
136
142
|
}
|
|
137
143
|
};
|
|
138
144
|
const custom = scope === "custom";
|
|
139
145
|
// The skew's sign and VIX beside IV30 are units of the figures above them, so they stay under the table.
|
|
140
|
-
const
|
|
146
|
+
const definitionLines = useMemo(() => vcaDefinitions({ skew: hasSkew }), [hasSkew]);
|
|
147
|
+
const definitions = useMemo(() => definitionLines.flatMap((line) => wrapTextLines(line, Math.max(1, width - 2))), [definitionLines, width]);
|
|
141
148
|
return <Box width={width} height={height} flexDirection="column" overflow="hidden">
|
|
142
149
|
<QueryBar width={width} filters={[
|
|
143
150
|
{ id: "universe", label: "Universe", value: VCA_SCOPE_OPTIONS.some((option) => option.value === scope) ? scope : "etfs",
|
|
@@ -159,7 +166,7 @@ export function IvScreenPane({ paneId, width, height, focused }: PaneProps) {
|
|
|
159
166
|
{nativePaneChrome ? (
|
|
160
167
|
// The desktop and the web wrap each definition at the pane's width, under a table that fills the rest.
|
|
161
168
|
<Box flexDirection="column" paddingX={1} flexShrink={0}>
|
|
162
|
-
{
|
|
169
|
+
{definitionLines.map((line) => <Prose key={line} text={line} width={Math.max(1, width - 2)} color={colors.textDim} figures={false} />)}
|
|
163
170
|
</Box>
|
|
164
171
|
) : (
|
|
165
172
|
<Box flexDirection="column" paddingX={1} height={definitions.length} flexShrink={0}>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fxVolatilityNote } from "../../../market-data/options-alternatives";
|
|
1
2
|
import type { InstrumentRef } from "../../../market-data/request-types";
|
|
2
3
|
import type { TickerRecord } from "../../../types/ticker";
|
|
3
4
|
import { canonicalExchange } from "../../../utils/exchanges";
|
|
@@ -33,7 +34,10 @@ export function vcaUniverse(scope: string, symbolsText: string, collectionId: st
|
|
|
33
34
|
instruments = VCA_PRESETS[preset].map((symbol) => ({ symbol, exchange: "" }));
|
|
34
35
|
}
|
|
35
36
|
const valid = [...new Map(instruments.filter((instrument) => SYMBOL.test(instrument.symbol)).map((instrument) => [instrument.symbol, instrument])).values()];
|
|
36
|
-
|
|
37
|
+
// An FX pair or currency future typed in a list is left out with what stands in for it, not as a symbol that does not parse.
|
|
38
|
+
const fx = scope === "custom" ? instruments.flatMap((instrument) => fxVolatilityNote(instrument.symbol, instrument.exchange) ?? []) : [];
|
|
39
|
+
const error = !valid.length ? fx[0] ?? (scope === "collection" ? "Link a watchlist or portfolio with US-listed symbols in pane settings." : "Add symbols in pane settings.")
|
|
40
|
+
: fx.length ? fx[0]!
|
|
37
41
|
: valid.length > VCA_LIMIT ? `Only the first ${VCA_LIMIT} symbols are screened.`
|
|
38
42
|
: skipped ? `${skipped} non-US listing${skipped === 1 ? "" : "s"} left out: stored implied volatility covers US option underlyings.` : null;
|
|
39
43
|
return { label, instruments: valid.slice(0, VCA_LIMIT), error };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
|
+
import { createTtlCache } from "../../../data/ttl-cache";
|
|
2
3
|
import { ApiRequestError } from "../../../api-client/errors";
|
|
3
4
|
import type {
|
|
4
5
|
CloudJobsMoversPayload,
|
|
@@ -25,43 +26,38 @@ export type JobsCompanyState =
|
|
|
25
26
|
| { kind: "denied"; status: 401 | 402 | 403; message: string };
|
|
26
27
|
|
|
27
28
|
const SUMMARY_TTL_MS = 5 * 60_000;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
/**
|
|
30
|
+
* `pending` is never kept, so the pane's poll reads it afresh each time, and
|
|
31
|
+
* `denied` is not either: the account can change under the pane.
|
|
32
|
+
*/
|
|
33
|
+
const summaries = createTtlCache<JobsCompanyState>({
|
|
34
|
+
ttlMs: SUMMARY_TTL_MS,
|
|
35
|
+
keep: (state) => state.kind === "ready" || state.kind === "uncovered",
|
|
36
|
+
});
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
ticker: string,
|
|
38
|
-
options: { name?: string | null; force?: boolean; client?: JobsCloudClient } = {},
|
|
39
|
-
): Promise<JobsCompanyState> {
|
|
40
|
-
const key = ticker.toUpperCase();
|
|
41
|
-
if (!options.force) {
|
|
42
|
-
const cached = cachedJobs(key);
|
|
43
|
-
// A pending answer is never served from cache: the point is to poll.
|
|
44
|
-
if (cached && cached.kind !== "pending") return cached;
|
|
45
|
-
}
|
|
46
|
-
const client = options.client ?? apiClient;
|
|
47
|
-
let state: JobsCompanyState;
|
|
38
|
+
async function loadJobs(key: string, name: string | null | undefined, client: JobsCloudClient): Promise<JobsCompanyState> {
|
|
48
39
|
try {
|
|
49
|
-
const response: CloudJobsResponse = await client.getCloudJobs(key,
|
|
50
|
-
|
|
40
|
+
const response: CloudJobsResponse = await client.getCloudJobs(key, name);
|
|
41
|
+
return response.status === "pending"
|
|
51
42
|
? { kind: "pending", ticker: key, message: response.message }
|
|
52
43
|
: { kind: "ready", summary: response };
|
|
53
44
|
} catch (error) {
|
|
54
45
|
if (error instanceof ApiRequestError && error.status === 404) {
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
46
|
+
return { kind: "uncovered", ticker: key, message: error.message };
|
|
47
|
+
}
|
|
48
|
+
if (error instanceof ApiRequestError && (error.status === 401 || error.status === 402 || error.status === 403)) {
|
|
58
49
|
return { kind: "denied", status: error.status, message: error.message };
|
|
59
|
-
} else {
|
|
60
|
-
throw error;
|
|
61
50
|
}
|
|
51
|
+
throw error;
|
|
62
52
|
}
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function fetchJobs(
|
|
56
|
+
ticker: string,
|
|
57
|
+
options: { name?: string | null; force?: boolean; client?: JobsCloudClient } = {},
|
|
58
|
+
): Promise<JobsCompanyState> {
|
|
59
|
+
const key = ticker.toUpperCase();
|
|
60
|
+
return summaries.load(key, () => loadJobs(key, options.name, options.client ?? apiClient), { force: options.force });
|
|
65
61
|
}
|
|
66
62
|
|
|
67
63
|
export function fetchJobsPostings(
|
|
@@ -81,5 +77,5 @@ export function fetchJobsMovers(
|
|
|
81
77
|
}
|
|
82
78
|
|
|
83
79
|
export function resetJobsCache(): void {
|
|
84
|
-
|
|
80
|
+
summaries.clear();
|
|
85
81
|
}
|
|
@@ -282,7 +282,7 @@ export function calculateKellySizing({
|
|
|
282
282
|
price?: number | null;
|
|
283
283
|
}): KellySizingResult {
|
|
284
284
|
const warnings: string[] = [];
|
|
285
|
-
if (!finite(
|
|
285
|
+
if (!finite(currentValue)) {
|
|
286
286
|
return {
|
|
287
287
|
mode,
|
|
288
288
|
bankroll: 0,
|
|
@@ -301,7 +301,7 @@ export function calculateKellySizing({
|
|
|
301
301
|
riskFraction: 0,
|
|
302
302
|
expectedReturn: 0,
|
|
303
303
|
expectedLogGrowth: 0,
|
|
304
|
-
warnings: [
|
|
304
|
+
warnings: ["Current holding value unavailable; check FX rates."],
|
|
305
305
|
clipReasons: [],
|
|
306
306
|
recommendationLabel: "Unavailable",
|
|
307
307
|
};
|
|
@@ -342,20 +342,24 @@ export function calculateKellySizing({
|
|
|
342
342
|
|
|
343
343
|
const safeCurrentValue = finite(currentValue) ? Math.max(0, currentValue) : 0;
|
|
344
344
|
const safePrice = price != null && finite(price) && price > 0 ? price : null;
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
const
|
|
345
|
+
// Sizes are shares of the bankroll; only the money amounts need one.
|
|
346
|
+
const hasBankroll = finite(bankroll) && bankroll > 0;
|
|
347
|
+
const targetValue = hasBankroll ? clippedFraction * bankroll : null;
|
|
348
|
+
const addTrimValue = targetValue == null ? null : targetValue - safeCurrentValue;
|
|
349
|
+
const estimatedUnits = safePrice && addTrimValue != null ? addTrimValue / safePrice : null;
|
|
348
350
|
const riskFraction = clippedFraction * downsideLossFraction;
|
|
349
|
-
const riskValue = riskFraction * bankroll;
|
|
351
|
+
const riskValue = hasBankroll ? riskFraction * bankroll : null;
|
|
352
|
+
// Nothing held is 0% of any bankroll; a holding with no bankroll has no share.
|
|
353
|
+
const currentFraction = hasBankroll ? safeCurrentValue / bankroll : safeCurrentValue > 0 ? null : 0;
|
|
350
354
|
|
|
351
355
|
if (fullKellyFraction === 0 && solve.expectedReturn <= 0) warnings.push("No positive Kelly edge.");
|
|
352
356
|
if (downsideLossFraction <= 0) warnings.push("No downside assumption; risk caps cannot bind.");
|
|
353
357
|
|
|
354
358
|
return {
|
|
355
359
|
mode,
|
|
356
|
-
bankroll,
|
|
360
|
+
bankroll: hasBankroll ? bankroll : 0,
|
|
357
361
|
currentValue: safeCurrentValue,
|
|
358
|
-
currentFraction
|
|
362
|
+
currentFraction,
|
|
359
363
|
price: safePrice,
|
|
360
364
|
fullKellyFraction,
|
|
361
365
|
fractionalKellyFraction,
|
|
@@ -372,7 +372,7 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
372
372
|
const sensitivity = useMemo(() => buildSensitivityGrid(sizingMode, activeDraft), [activeDraft, sizingMode]);
|
|
373
373
|
const curveMaxFraction = useMemo(
|
|
374
374
|
() => getKellyCurveMaxFraction(sizingMode, activeDraft, [
|
|
375
|
-
result.currentFraction,
|
|
375
|
+
result.currentFraction ?? 0,
|
|
376
376
|
result.clippedFraction,
|
|
377
377
|
result.fullKellyFraction,
|
|
378
378
|
]),
|
|
@@ -384,7 +384,9 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
384
384
|
);
|
|
385
385
|
const curveXAxisLabels = useMemo(() => buildKellyCurveXAxisLabels(curveMaxFraction), [curveMaxFraction]);
|
|
386
386
|
const currentGrowth = useMemo(
|
|
387
|
-
() =>
|
|
387
|
+
() => result.currentFraction == null
|
|
388
|
+
? null
|
|
389
|
+
: calculateExpectedLogGrowthAtFraction(sizingMode, activeDraft, result.currentFraction),
|
|
388
390
|
[activeDraft, sizingMode, result.currentFraction],
|
|
389
391
|
);
|
|
390
392
|
const targetGrowth = useMemo(
|
|
@@ -393,15 +395,16 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
393
395
|
);
|
|
394
396
|
const curveMarkers = useMemo<StaticChartXMarker[]>(() => {
|
|
395
397
|
if (!Number.isFinite(curveMaxFraction) || curveMaxFraction <= 0) return [];
|
|
396
|
-
|
|
398
|
+
// Caps apply in turn, so with both binding the last one set the target.
|
|
399
|
+
const targetLabel = result.clipReasons.at(-1)?.replace(/^max /, "") ?? "target";
|
|
397
400
|
const markers: StaticChartXMarker[] = [
|
|
398
|
-
{
|
|
401
|
+
...(result.currentFraction == null ? [] : [{
|
|
399
402
|
id: "current",
|
|
400
403
|
xRatio: result.currentFraction / curveMaxFraction,
|
|
401
404
|
label: "current",
|
|
402
405
|
color: colors.textDim,
|
|
403
406
|
lineChar: "┊",
|
|
404
|
-
},
|
|
407
|
+
}]),
|
|
405
408
|
{
|
|
406
409
|
id: "target",
|
|
407
410
|
xRatio: result.clippedFraction / curveMaxFraction,
|
|
@@ -474,9 +477,6 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
474
477
|
usePaneFooter(KELLY_PANE_ID, () => ({
|
|
475
478
|
info: historyStatus
|
|
476
479
|
? [{ id: "history", parts: [historyStatus] }]
|
|
477
|
-
: bankroll <= 0 && ticker
|
|
478
|
-
// Every size reads 0% until there is a bankroll; say so where status lives.
|
|
479
|
-
? [{ id: "bankroll", parts: [{ text: "no bankroll", tone: "warning" as const }] }]
|
|
480
480
|
: result.warnings.length > 0
|
|
481
481
|
? [{ id: "warning", parts: [{ text: result.warnings[0]!, tone: "warning" as const }] }]
|
|
482
482
|
: result.clipReasons.length > 0
|
|
@@ -488,7 +488,7 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
488
488
|
{ id: "search", key: "/", label: "search", onPress: focusTickerSearch },
|
|
489
489
|
...(viewInBar ? [] : [{ id: "sensitivity", key: "s", label: showSensitivity ? "ensitivity off" : "ensitivity", onPress: toggleSensitivity }]),
|
|
490
490
|
],
|
|
491
|
-
}), [activeInputId,
|
|
491
|
+
}), [activeInputId, editSelectedField, focusTickerSearch, historyStatus?.text, historyStatus?.tone, result.clipReasons, result.warnings, selectedField?.id, selectedField?.label, showSensitivity, ticker, toggleSensitivity, viewInBar]);
|
|
492
492
|
|
|
493
493
|
const portfolioTabs = useMemo(
|
|
494
494
|
() => config.portfolios.map((portfolio) => ({ label: portfolio.name, value: portfolio.id })),
|
|
@@ -11,7 +11,7 @@ import { resolveChartPalette } from "../../../components/chart/core/palette";
|
|
|
11
11
|
import type { StaticChartXMarker } from "../../../components/chart/static";
|
|
12
12
|
import { colors, priceColor } from "../../../theme/colors";
|
|
13
13
|
import { Box } from "../../../ui";
|
|
14
|
-
import { displayWidth, formatCurrency, formatNumber } from "../../../utils/format";
|
|
14
|
+
import { displayWidth, formatCurrency, formatNumber, MISSING_VALUE } from "../../../utils/format";
|
|
15
15
|
import type { KellySizingResult, SensitivityGrid } from "./types";
|
|
16
16
|
import type { KellyHistoryInputs } from "./history";
|
|
17
17
|
import { formatPct, formatSignedPct } from "./view";
|
|
@@ -32,10 +32,15 @@ export function buildKellyHistoryItems(inputs: KellyHistoryInputs): StatItem[] {
|
|
|
32
32
|
];
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** What the first money figure says while there is no bankroll to size in. */
|
|
36
|
+
const BANKROLL_HINT = "Set a bankroll";
|
|
37
|
+
|
|
35
38
|
/**
|
|
36
39
|
* The sizing result as one band under the inputs, laid out in the FieldGrid's
|
|
37
40
|
* columns: the Kelly fractions, then the move in money, then risk, then log
|
|
38
|
-
* growth at full Kelly, at the current size and at the target.
|
|
41
|
+
* growth at full Kelly, at the current size and at the target. Money figures
|
|
42
|
+
* read a dash until there is a bankroll; the fractions and growth never wait
|
|
43
|
+
* for one.
|
|
39
44
|
*/
|
|
40
45
|
export function buildKellyResultItems({
|
|
41
46
|
result,
|
|
@@ -47,12 +52,13 @@ export function buildKellyResultItems({
|
|
|
47
52
|
}: {
|
|
48
53
|
result: KellySizingResult;
|
|
49
54
|
baseCurrency: string;
|
|
50
|
-
|
|
55
|
+
/** Null when the holding is not a share of any bankroll. */
|
|
56
|
+
currentGrowth: number | null;
|
|
51
57
|
targetGrowth: number;
|
|
52
58
|
width: number;
|
|
53
59
|
columns: number;
|
|
54
60
|
}): StatItem[] {
|
|
55
|
-
const addTrim = formatCurrency(result.addTrimValue, baseCurrency);
|
|
61
|
+
const addTrim = result.addTrimValue == null ? MISSING_VALUE : formatCurrency(result.addTrimValue, baseCurrency);
|
|
56
62
|
const units = result.estimatedUnits == null ? null : `${formatNumber(result.estimatedUnits, 1)} units`;
|
|
57
63
|
// The units ride beside the amount when the column has room for label,
|
|
58
64
|
// amount and units (plus the cell inset and gaps); otherwise they take a
|
|
@@ -68,14 +74,16 @@ export function buildKellyResultItems({
|
|
|
68
74
|
value: formatPct(result.clippedFraction, 2),
|
|
69
75
|
tone: result.clipReasons.length > 0 ? "positive" : undefined,
|
|
70
76
|
},
|
|
71
|
-
{ id: "current", label: "Current %", value: formatPct(result.currentFraction, 1) },
|
|
72
|
-
|
|
77
|
+
{ id: "current", label: "Current %", value: result.currentFraction == null ? MISSING_VALUE : formatPct(result.currentFraction, 1) },
|
|
78
|
+
result.targetValue == null
|
|
79
|
+
? { id: "target", label: "Target val", value: MISSING_VALUE, detail: BANKROLL_HINT }
|
|
80
|
+
: { id: "target", label: "Target val", value: formatCurrency(result.targetValue, baseCurrency) },
|
|
73
81
|
{
|
|
74
82
|
id: "addTrim",
|
|
75
83
|
label: "Add / trim",
|
|
76
84
|
value: addTrim,
|
|
77
85
|
detail: unitsInline ? units ?? undefined : undefined,
|
|
78
|
-
color: priceColor(result.addTrimValue),
|
|
86
|
+
color: result.addTrimValue == null ? undefined : priceColor(result.addTrimValue),
|
|
79
87
|
},
|
|
80
88
|
...(units != null && !unitsInline
|
|
81
89
|
? [{ id: "units", label: "Units", value: formatNumber(result.estimatedUnits!, 1) }]
|
|
@@ -83,9 +91,10 @@ export function buildKellyResultItems({
|
|
|
83
91
|
{
|
|
84
92
|
id: "risk",
|
|
85
93
|
label: "Risk",
|
|
86
|
-
value: formatCurrency(result.riskValue, baseCurrency),
|
|
94
|
+
value: result.riskValue == null ? MISSING_VALUE : formatCurrency(result.riskValue, baseCurrency),
|
|
87
95
|
detail: formatPct(result.riskFraction, 2),
|
|
88
|
-
|
|
96
|
+
// Nothing at risk is not a loss.
|
|
97
|
+
tone: result.riskValue != null && result.riskValue > 0 ? "negative" : undefined,
|
|
89
98
|
},
|
|
90
99
|
{ id: "worstLoss", label: "Worst loss", value: formatPct(result.downsideLossFraction, 1) },
|
|
91
100
|
{
|
|
@@ -95,7 +104,7 @@ export function buildKellyResultItems({
|
|
|
95
104
|
color: priceColor(result.expectedReturn),
|
|
96
105
|
},
|
|
97
106
|
{ id: "fullGrowth", label: "Full growth", value: formatSignedPct(result.expectedLogGrowth) },
|
|
98
|
-
{ id: "growthNow", label: "Growth now", value: formatSignedPct(currentGrowth) },
|
|
107
|
+
{ id: "growthNow", label: "Growth now", value: currentGrowth == null ? MISSING_VALUE : formatSignedPct(currentGrowth) },
|
|
99
108
|
{ id: "growthTarget", label: "Growth tgt", value: formatSignedPct(targetGrowth) },
|
|
100
109
|
];
|
|
101
110
|
}
|
|
@@ -69,25 +69,31 @@ export interface KellySolveResult {
|
|
|
69
69
|
warning?: string;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The fractions, expected return and growth need no bankroll. The money fields
|
|
74
|
+
* (target, add or trim, units, risk) are null until there is one, and
|
|
75
|
+
* `currentFraction` is null for a holding that has no bankroll to be a share of.
|
|
76
|
+
*/
|
|
72
77
|
export interface KellySizingResult {
|
|
73
78
|
mode: KellySizingMode;
|
|
74
79
|
bankroll: number;
|
|
75
80
|
currentValue: number;
|
|
76
|
-
currentFraction: number;
|
|
81
|
+
currentFraction: number | null;
|
|
77
82
|
price: number | null;
|
|
78
83
|
fullKellyFraction: number;
|
|
79
84
|
fractionalKellyFraction: number;
|
|
80
85
|
unclippedFraction: number;
|
|
81
86
|
clippedFraction: number;
|
|
82
|
-
targetValue: number;
|
|
83
|
-
addTrimValue: number;
|
|
87
|
+
targetValue: number | null;
|
|
88
|
+
addTrimValue: number | null;
|
|
84
89
|
estimatedUnits: number | null;
|
|
85
90
|
downsideLossFraction: number;
|
|
86
|
-
riskValue: number;
|
|
91
|
+
riskValue: number | null;
|
|
87
92
|
riskFraction: number;
|
|
88
93
|
expectedReturn: number;
|
|
89
94
|
expectedLogGrowth: number;
|
|
90
95
|
warnings: string[];
|
|
96
|
+
/** The caps that cut the size, in the order applied; the last one set the target. */
|
|
91
97
|
clipReasons: string[];
|
|
92
98
|
recommendationLabel: string;
|
|
93
99
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
2
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
3
3
|
import { loadMacroDayEarnings, loadMacroDayHistory, loadMacroReleases, macroDayEarningsNotice } from "./client";
|
|
4
|
-
import { macroDayEventLabel, macroDayStats, matchesSurprise, projectMacroDays, type MacroDayStats } from "./model";
|
|
4
|
+
import { formatMacroDayMove, macroDayEventLabel, macroDayStats, matchesSurprise, projectMacroDays, type MacroDayStats } from "./model";
|
|
5
5
|
import { MACRO_EVENT_KINDS, MACRO_EVENT_LABELS, MACRO_RELEASE_SOURCES, MACRO_SURPRISE_LABELS, MACRO_SURPRISES, staleMacroReleasesNotice,
|
|
6
6
|
type MacroEventKind, type MacroSurprise } from "./releases";
|
|
7
7
|
|
|
@@ -39,6 +39,12 @@ export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
39
39
|
releases: releases.releases, coveredThrough: releases.coveredThrough, earnings: earnings.reports });
|
|
40
40
|
const notes = [staleMacroReleasesNotice(releases), macroDayEarningsNotice(earnings)].filter((note): note is string => !!note);
|
|
41
41
|
const row = (label: string, stats: MacroDayStats) => ({ label, ...stats });
|
|
42
|
+
// A yield index (^TNX) moves by its level: its columns are headed bp and read the change in the yield.
|
|
43
|
+
const yieldMoves = model.basis === "yield";
|
|
44
|
+
const bp = (signed: boolean) => (value: unknown) => typeof value === "number" ? formatMacroDayMove(value, "yield", { signed, unit: false }) : "--";
|
|
45
|
+
const move = yieldMoves ? bp(true) : percent;
|
|
46
|
+
const absMove = yieldMoves ? bp(false) : percent;
|
|
47
|
+
const header = (label: string) => yieldMoves ? `${label} bp` : label;
|
|
42
48
|
const release = String(args.options.release ?? "all");
|
|
43
49
|
const surprise = String(args.options.surprise ?? "all") as MacroSurprise | "all";
|
|
44
50
|
const events = model.events.filter((event) => (release === "all" || event.kind === release) && matchesSurprise(event, surprise));
|
|
@@ -48,14 +54,14 @@ export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
48
54
|
return {
|
|
49
55
|
sections: [
|
|
50
56
|
{ title: "By release", columns: [{ key: "label", header: "Day" }, { key: "count", header: "Days" },
|
|
51
|
-
{ key: "meanAbs", header: "Avg |move|", format:
|
|
52
|
-
{ key: "mean", header: "Avg move", format:
|
|
57
|
+
{ key: "meanAbs", header: header("Avg |move|"), format: absMove }, { key: "multiple", header: "vs normal", format: multiple },
|
|
58
|
+
{ key: "mean", header: header("Avg move"), format: move }, { key: "hitRate", header: "Up", format: share }],
|
|
53
59
|
rows: [...model.kinds.map((kind) => row(MACRO_EVENT_LABELS[kind], model.byKind[kind])), ...selected,
|
|
54
60
|
row(model.kinds.includes("ipca") ? "Any US release" : "Any release", model.allEvents), ...(model.earnings.count ? [row("Earnings", model.earnings)] : []), row("Normal day", model.normal)] },
|
|
55
61
|
{ title: "Release days", columns: [{ key: "date", header: "Date" }, { key: "event", header: "Release" },
|
|
56
62
|
{ key: "actual", header: "Actual" }, { key: "forecast", header: "Forecast" }, { key: "prior", header: "Prior" },
|
|
57
63
|
{ key: "surprise", header: "Surprise" },
|
|
58
|
-
{ key: "session", header: "Session" }, { key: "move", header: "Move", format:
|
|
64
|
+
{ key: "session", header: "Session" }, { key: "move", header: header("Move"), format: move }, { key: "multiple", header: "vs normal", format: multiple }],
|
|
59
65
|
rows: events.map(({ figures, ...event }) => ({ ...event, event: macroDayEventLabel(event), actual: figures?.actual ?? null,
|
|
60
66
|
forecast: figures?.forecast ?? null, prior: figures?.prior ?? null, surprise: figures?.surprise ?? null,
|
|
61
67
|
...(figures ? { headline: figures.event } : {}) })) },
|
|
@@ -64,7 +70,8 @@ export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
64
70
|
unavailableSymbols: model.events.length ? [] : [instrument.symbol],
|
|
65
71
|
errors: history.error ? [history.error] : [],
|
|
66
72
|
...(notes.length ? { notes } : {}),
|
|
67
|
-
metadata: { unit: "
|
|
73
|
+
metadata: { unit: yieldMoves ? "change in the yield in percentage points, close to close on the release day"
|
|
74
|
+
: "decimal return, close to close on the release day", start: model.start, asOf: model.asOf,
|
|
68
75
|
releasesThrough: releases.coveredThrough, releaseSources: Object.values(MACRO_RELEASE_SOURCES),
|
|
69
76
|
stale: history.stale || earnings.stale, fetchedAt: history.fetchedAt, methodology: "docs/research-data.md#macro-day-reaction" },
|
|
70
77
|
};
|
|
@@ -16,7 +16,7 @@ export const macroDayModule: PluginModule = {
|
|
|
16
16
|
paneTemplates: [{ ...createTickerSurfacePaneTemplate({
|
|
17
17
|
id: "macro-day-pane", paneId: "macro-day", label: "Macro-Day Reaction",
|
|
18
18
|
description: "How a name moves on CPI, jobs, FOMC and Brazil IPCA days against a normal day: average move, direction, hit rate, and every release day with its actual, forecast and prior.",
|
|
19
|
-
keywords: ["mday", "macro day", "cpi day", "fomc day", "jobs day", "nfp", "payrolls", "event study", "release reaction", "ipca",
|
|
19
|
+
keywords: ["mday", "macro day", "cpi day", "fomc day", "jobs day", "jobs report", "nfp", "payrolls", "nonfarm", "event study", "release reaction", "ipca",
|
|
20
20
|
"hot cpi", "cpi surprise", "release surprise"],
|
|
21
21
|
shortcut: "MDAY", publicShare: true,
|
|
22
22
|
}), headless: macroDayHeadless }],
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { EarningsTiming } from "../../../api-client/earnings";
|
|
2
2
|
import type { PricePoint } from "../../../types/financials";
|
|
3
3
|
import { zonedDateTimeParts } from "../../../utils/zoned-date-time";
|
|
4
|
+
import { toBasisPoints } from "../../../utils/basis-points";
|
|
5
|
+
import { isYieldIndexSymbol } from "../../../tickers/index-roots";
|
|
4
6
|
import { MACRO_EVENT_KINDS, MACRO_EVENT_LABELS, US_MACRO_EVENT_KINDS, type MacroEventKind, type MacroRelease, type MacroReleaseFigures,
|
|
5
7
|
type MacroSurprise } from "./releases";
|
|
6
8
|
|
|
@@ -17,6 +19,35 @@ export interface MacroDayEarningsReport {
|
|
|
17
19
|
reactionSession?: string | null;
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
/**
|
|
23
|
+
* How a day's move is measured: a price's return, or for a yield (^TNX, whose
|
|
24
|
+
* level is the yield in percent) the change in that yield, in percentage
|
|
25
|
+
* points, which the pane and the report read in basis points.
|
|
26
|
+
*/
|
|
27
|
+
export type MacroDayMoveBasis = "return" | "yield";
|
|
28
|
+
|
|
29
|
+
/** A Treasury yield index moves by its level: 5.24 to 5.28 is +4bp, not +0.76%. */
|
|
30
|
+
const macroDayMoveBasis = (symbol: string): MacroDayMoveBasis => isYieldIndexSymbol(symbol) ? "yield" : "return";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A move as it reads: a return in percent (`+0.76%`), a yield's change in
|
|
34
|
+
* basis points (`+4.0bp`, or `+4.0` under a column headed bp). One that
|
|
35
|
+
* rounds to zero is unsigned.
|
|
36
|
+
*/
|
|
37
|
+
export function formatMacroDayMove(value: number | null | undefined, basis: MacroDayMoveBasis,
|
|
38
|
+
{ signed = true, unit = true }: { signed?: boolean; unit?: boolean } = {}): string {
|
|
39
|
+
if (value == null) return "--";
|
|
40
|
+
if (basis === "yield") {
|
|
41
|
+
const bp = toBasisPoints(value, 1);
|
|
42
|
+
return `${signed && bp > 0 ? "+" : ""}${bp.toFixed(1)}${unit ? "bp" : ""}`;
|
|
43
|
+
}
|
|
44
|
+
const fixed = (value * 100).toFixed(2);
|
|
45
|
+
return /[1-9]/.test(fixed) ? `${signed && value > 0 ? "+" : ""}${fixed}%` : "0.00%";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The decimals a move shows in hundredths (percent of a return, basis points of a yield), for its sign and colour. */
|
|
49
|
+
export const macroDayMoveDecimals = (basis: MacroDayMoveBasis) => basis === "yield" ? 1 : 2;
|
|
50
|
+
|
|
20
51
|
export interface MacroDayEvent {
|
|
21
52
|
kind: MacroEventKind;
|
|
22
53
|
/** Release day, New York date. */
|
|
@@ -25,7 +56,7 @@ export interface MacroDayEvent {
|
|
|
25
56
|
session: string;
|
|
26
57
|
/** The session is also the reaction session of the ticker's own earnings report: the move is the report's, not the release's. */
|
|
27
58
|
earnings: boolean;
|
|
28
|
-
/** Close before the release to the session's close. */
|
|
59
|
+
/** Close before the release to the session's close: a return, or a yield's change in percentage points (`basis`). */
|
|
29
60
|
move: number;
|
|
30
61
|
/** |move| over the normal day's average |move|. */
|
|
31
62
|
multiple: number | null;
|
|
@@ -45,6 +76,7 @@ export interface MacroDayStats {
|
|
|
45
76
|
|
|
46
77
|
export interface MacroDayModel {
|
|
47
78
|
symbol: string;
|
|
79
|
+
basis: MacroDayMoveBasis;
|
|
48
80
|
/** Newest first. */
|
|
49
81
|
events: MacroDayEvent[];
|
|
50
82
|
/** The kinds the list carries: the US releases, and IPCA when Gloom Cloud lists its days. */
|
|
@@ -140,17 +172,19 @@ export function projectMacroDays(history: readonly PricePoint[], options: {
|
|
|
140
172
|
earnings?: readonly MacroDayEarningsReport[];
|
|
141
173
|
}): MacroDayModel {
|
|
142
174
|
const { coveredThrough } = options;
|
|
175
|
+
const basis = macroDayMoveBasis(options.symbol);
|
|
143
176
|
const byDate = new Map<string, number>();
|
|
144
177
|
for (const point of history) {
|
|
145
178
|
const time = new Date(point.date).getTime();
|
|
146
|
-
|
|
179
|
+
// A yield can sit at zero or below it; a price cannot.
|
|
180
|
+
if (!Number.isFinite(time) || !Number.isFinite(point.close) || (basis === "return" && point.close <= 0)) continue;
|
|
147
181
|
byDate.set(sessionDate(time), point.close);
|
|
148
182
|
}
|
|
149
183
|
const sessions = [...byDate.keys()].sort();
|
|
150
184
|
const last = sessions.at(-1) ?? null;
|
|
151
185
|
const empty = (): MacroDayStats => stats([], null);
|
|
152
186
|
const kinds = MACRO_EVENT_KINDS.filter((kind) => US_KINDS.has(kind) || options.releases.some((release) => release.kind === kind));
|
|
153
|
-
if (!last) return { symbol: options.symbol, events: [], kinds, byKind: { cpi: empty(), jobs: empty(), fomc: empty(), ipca: empty() },
|
|
187
|
+
if (!last) return { symbol: options.symbol, basis, events: [], kinds, byKind: { cpi: empty(), jobs: empty(), fomc: empty(), ipca: empty() },
|
|
154
188
|
allEvents: empty(), earnings: empty(), normal: empty(), start: null, asOf: null };
|
|
155
189
|
|
|
156
190
|
// Sessions after the published lists end may be unlisted release days, so they are left out of both sides.
|
|
@@ -166,7 +200,7 @@ export function projectMacroDays(history: readonly PricePoint[], options: {
|
|
|
166
200
|
for (let index = 1; index < sessions.length; index++) {
|
|
167
201
|
const date = sessions[index]!, previous = sessions[index - 1]!;
|
|
168
202
|
if (date <= start || date > end || dayNumber(date) - dayNumber(previous) > MAX_SESSION_GAP_DAYS) continue;
|
|
169
|
-
moves.set(date, byDate.get(date)! / byDate.get(previous)! - 1);
|
|
203
|
+
moves.set(date, basis === "yield" ? byDate.get(date)! - byDate.get(previous)! : byDate.get(date)! / byDate.get(previous)! - 1);
|
|
170
204
|
}
|
|
171
205
|
|
|
172
206
|
const reactionSessions = new Set<string>();
|
|
@@ -191,7 +225,7 @@ export function projectMacroDays(history: readonly PricePoint[], options: {
|
|
|
191
225
|
const byKind = Object.fromEntries(MACRO_EVENT_KINDS.map((kind) =>
|
|
192
226
|
[kind, stats(events.filter((event) => event.kind === kind && !event.earnings).map((event) => event.move), normalAbs)])) as Record<MacroEventKind, MacroDayStats>;
|
|
193
227
|
const sessionMoves = (flagged: boolean) => [...reactionSessions].filter((session) => reacted.has(session) === flagged).map((session) => moves.get(session)!);
|
|
194
|
-
return { symbol: options.symbol, events, kinds, byKind, allEvents: stats(sessionMoves(false), normalAbs), earnings: stats(sessionMoves(true), normalAbs),
|
|
228
|
+
return { symbol: options.symbol, basis, events, kinds, byKind, allEvents: stats(sessionMoves(false), normalAbs), earnings: stats(sessionMoves(true), normalAbs),
|
|
195
229
|
normal, start, asOf: end };
|
|
196
230
|
}
|
|
197
231
|
|