gloomberb 0.18.4 → 0.19.0
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 +169 -5
- package/src/account/auth-model.ts +7 -3
- package/src/api-client/auth.ts +9 -4
- package/src/api-client/catalysts.ts +2 -1
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +31 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +171 -11
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +13 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/request.ts +222 -41
- package/src/api-client/research-activity.ts +6 -12
- package/src/api-client/types.ts +74 -0
- package/src/app/keybindings/index.ts +3 -0
- package/src/app/keybindings/labels.ts +18 -1
- package/src/app/keybindings/resolve.ts +14 -0
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +42 -27
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +1 -1
- 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/fred-series.ts +5 -0
- package/src/cli/commands/keybindings.ts +23 -0
- package/src/cli/commands/market.ts +138 -46
- package/src/cli/commands/options-chain.ts +107 -10
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +176 -39
- package/src/cli/commands/search.ts +39 -3
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +57 -35
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +10 -2
- package/src/cli/export-source.ts +29 -1
- package/src/cli/glossary.ts +73 -1
- package/src/cli/history-rows.ts +87 -16
- package/src/cli/index.ts +2 -0
- package/src/cli/known-gaps.ts +5 -2
- package/src/cli/listing-arg.ts +195 -56
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +111 -16
- package/src/cli/pane-functions/function-help.ts +23 -13
- package/src/cli/pane-functions/headless.ts +56 -12
- package/src/cli/pane-functions/index.ts +58 -16
- package/src/cli/pane-functions/market-session.ts +81 -9
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/report.ts +5 -1
- package/src/cli/pane-functions/resolver.ts +35 -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 +30 -13
- package/src/cli/report-tables.ts +18 -7
- package/src/cli/restore-plugins.ts +13 -1
- package/src/cli/result.ts +57 -5
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +26 -4
- package/src/components/chart/composite/format.ts +27 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/levels.ts +2 -0
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +61 -5
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +46 -7
- package/src/components/chart/composite/series-axis-tags.tsx +16 -9
- 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/types.ts +4 -0
- 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/pane-templates/items.ts +3 -2
- package/src/components/command-bar/panel/prompt-binding.ts +0 -8
- package/src/components/command-bar/workflow/alert.ts +1 -1
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/header.tsx +26 -40
- package/src/components/layout/pane/footer/index.tsx +1 -0
- package/src/components/layout/pane/footer/model.ts +27 -0
- package/src/components/onboarding/onboarding-frame.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
- 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 +4 -1
- 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 +5 -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/core/state/app/layout.ts +2 -0
- package/src/core/state/app/state.ts +9 -0
- package/src/core/state/app/types.ts +3 -0
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/saved-copies.ts +55 -16
- 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 +427 -216
- package/src/i18n/es.ts +19 -3
- package/src/i18n/ja.ts +19 -3
- package/src/i18n/ko.ts +19 -3
- package/src/i18n/zh-cn.ts +19 -3
- package/src/i18n/zh-tw.ts +19 -3
- package/src/market-data/coordinator/financials.ts +10 -2
- package/src/market-data/coordinator/index.ts +3 -1
- package/src/market-data/coordinator/quotes.ts +4 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +48 -7
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/status.ts +16 -4
- package/src/market-data/options-alternatives.ts +141 -4
- package/src/market-data/quotes/contributions.ts +20 -2
- package/src/market-data/quotes/resolution.ts +3 -0
- package/src/market-data/selectors.ts +3 -0
- package/src/news/aggregator.ts +113 -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 +11 -0
- package/src/notifications/notification-log.ts +6 -4
- package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +380 -82
- package/src/plugins/builtin/alerts/command.ts +31 -0
- package/src/plugins/builtin/alerts/constants.ts +1 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- package/src/plugins/builtin/alerts/fired-log.ts +113 -8
- package/src/plugins/builtin/alerts/format.ts +80 -4
- package/src/plugins/builtin/alerts/headless.ts +30 -7
- package/src/plugins/builtin/alerts/history-rows.ts +7 -3
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +135 -22
- package/src/plugins/builtin/alerts/live.ts +3 -3
- package/src/plugins/builtin/alerts/metric-command.ts +134 -0
- package/src/plugins/builtin/alerts/metric-data.ts +111 -0
- package/src/plugins/builtin/alerts/metric-model.ts +324 -0
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
- package/src/plugins/builtin/alerts/pane.tsx +90 -39
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +37 -10
- package/src/plugins/builtin/alerts/types.ts +7 -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/backtest/headless.ts +10 -5
- package/src/plugins/builtin/backtest/pane.tsx +9 -4
- package/src/plugins/builtin/backtest/presets.ts +29 -3
- package/src/plugins/builtin/broker-manager/index.tsx +10 -5
- package/src/plugins/builtin/broker-manager/model.ts +6 -1
- package/src/plugins/builtin/broker-trading/controller.ts +22 -12
- package/src/plugins/builtin/broker-trading/index.tsx +13 -1
- package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
- package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
- 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 +7 -3
- package/src/plugins/builtin/catalysts/model.ts +2 -1
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
- package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
- package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
- package/src/plugins/builtin/chart-composer/formula.ts +71 -18
- package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
- package/src/plugins/builtin/chart-composer/headless.ts +19 -3
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
- package/src/plugins/builtin/chart-composer/presets.ts +50 -15
- package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
- package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
- package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
- package/src/plugins/builtin/chat/content/composer.tsx +13 -6
- package/src/plugins/builtin/chat/content/footer.tsx +6 -3
- package/src/plugins/builtin/chat/content/index.tsx +50 -21
- package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
- package/src/plugins/builtin/chat/index.tsx +23 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
- package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
- package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -1
- 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/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -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 +7 -7
- 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/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/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/calendar-model.ts +14 -9
- package/src/plugins/builtin/econ/figures.ts +72 -0
- package/src/plugins/builtin/econ/index.tsx +33 -3
- package/src/plugins/builtin/econ/types.ts +4 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
- package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
- package/src/plugins/builtin/econ-statistics/view.ts +3 -0
- 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 +70 -15
- package/src/plugins/builtin/exposure/charts.tsx +2 -2
- package/src/plugins/builtin/exposure/model.ts +13 -1
- package/src/plugins/builtin/exposure/pane.tsx +4 -4
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/futures/model.ts +2 -1
- package/src/plugins/builtin/futures/table.tsx +2 -2
- 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 +11 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
- package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
- package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -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 +1 -0
- package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- 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 +36 -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/pane.tsx +7 -5
- 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 +25 -7
- package/src/plugins/builtin/market-movers/footer.ts +2 -2
- package/src/plugins/builtin/market-movers/headless.ts +32 -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 +14 -0
- 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 +82 -0
- package/src/plugins/builtin/members/client.ts +19 -4
- package/src/plugins/builtin/members/headless.ts +20 -7
- package/src/plugins/builtin/members/index.tsx +9 -9
- package/src/plugins/builtin/members/model.ts +23 -4
- package/src/plugins/builtin/members/pane.tsx +25 -15
- package/src/plugins/builtin/news/index.tsx +1 -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 +54 -6
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/model-spot.ts +5 -2
- package/src/plugins/builtin/options/strike-window.ts +2 -1
- package/src/plugins/builtin/options/table.ts +6 -9
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +42 -12
- package/src/plugins/builtin/options-positioning/client.ts +30 -0
- package/src/plugins/builtin/options-positioning/headless.ts +74 -21
- package/src/plugins/builtin/options-positioning/index.tsx +4 -4
- package/src/plugins/builtin/options-positioning/model.ts +122 -14
- package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
- 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/freshness.ts +12 -10
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/live-rows.ts +10 -3
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +32 -31
- 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 +12 -7
- package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
- 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/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/rate-path/pane.tsx +2 -2
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- 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 +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
- 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/scanner/feed.ts +2 -2
- 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 +40 -6
- package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
- package/src/plugins/builtin/scanner/live-feed.ts +168 -0
- package/src/plugins/builtin/scanner/live-headless.ts +138 -0
- package/src/plugins/builtin/scanner/live-model.ts +362 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
- package/src/plugins/builtin/scanner/live-track.ts +186 -0
- 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 +2 -1
- package/src/plugins/builtin/series-inventory.ts +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- 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/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/macro-releases.ts +65 -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 +3 -2
- package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
- 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 +10 -3
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/headless.ts +6 -4
- package/src/plugins/builtin/themes/model.ts +17 -8
- package/src/plugins/builtin/themes/pane.tsx +7 -7
- 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 +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
- 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-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/charts.tsx +9 -4
- package/src/plugins/builtin/vol-surface/client.ts +9 -17
- package/src/plugins/builtin/vol-surface/headless.ts +63 -14
- package/src/plugins/builtin/vol-surface/index.tsx +2 -2
- package/src/plugins/builtin/vol-surface/model.ts +22 -7
- package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
- package/src/plugins/builtin/vol-surface/tables.ts +31 -3
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +4 -4
- package/src/plugins/builtin/volatility/series.ts +26 -0
- package/src/plugins/builtin/world-indices/footer.ts +1 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-browser.ts +13 -8
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +5 -5
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/remote/resources.ts +2 -1
- package/src/renderers/browser/app-services.ts +1 -7
- package/src/renderers/browser/config-host.ts +2 -55
- package/src/renderers/browser/main.tsx +7 -57
- package/src/renderers/dom/data-table/row.tsx +26 -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 +222 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
- package/src/renderers/electrobun/bun/index.ts +25 -1
- package/src/renderers/electrobun/shared/app-name.ts +30 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/renderers/opentui/toast-host.tsx +71 -43
- 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/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
- package/src/sources/provider-router/batches.ts +10 -2
- package/src/sources/provider-router/financial-routes.ts +3 -3
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/history.ts +28 -7
- package/src/sources/provider-router/index.ts +149 -10
- package/src/sources/provider-router/search.ts +13 -2
- package/src/state/app/context/index.tsx +3 -0
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/auth-transition.ts +19 -9
- package/src/sync/baseline.ts +10 -4
- package/src/sync/controller.ts +70 -19
- package/src/sync/core-contributors.ts +90 -31
- package/src/sync/react.ts +4 -1
- package/src/sync/types.ts +3 -0
- 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/telemetry/usage-counts.ts +0 -18
- package/src/test-support/preload.ts +14 -0
- package/src/theme/colors.ts +8 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name.ts +103 -3
- package/src/tickers/listing-venues.ts +38 -3
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/index.ts +36 -7
- package/src/tickers/search/ranking.ts +10 -1
- package/src/tickers/search/types.ts +5 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/base-series.ts +13 -5
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +2 -4
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/forward-valuation.ts +32 -0
- package/src/time-series/fundamentals.ts +198 -7
- package/src/time-series/hooks.ts +1 -0
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/price-units.ts +72 -2
- package/src/time-series/resolve-context.ts +7 -1
- package/src/time-series/resolve.ts +116 -15
- package/src/time-series/series-presentation.ts +40 -6
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +5 -3
- package/src/time-series/studies.ts +278 -74
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +3 -3
- package/src/time-series/types.ts +27 -1
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/data-provider.ts +9 -1
- package/src/types/desktop-window.ts +2 -0
- package/src/types/financials.ts +73 -2
- package/src/types/headless.ts +61 -5
- package/src/types/instrument.ts +3 -0
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +10 -1
- package/src/ui/host.tsx +3 -0
- package/src/updater/command-names.ts +12 -7
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +44 -0
- package/src/utils/financial-statements.ts +6 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- 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/network-failure.ts +9 -6
- package/src/utils/option-expiry.ts +48 -1
- package/src/utils/options.ts +10 -0
- package/src/utils/plugin-proxy-hosts.json +1 -0
- package/src/utils/price-history.ts +12 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
- package/src/renderers/browser/first-screen.ts +0 -193
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { PerpEquityRow } from "../../perps/equity-row";
|
|
2
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
4
|
import { Box, useUiCapabilities } from "../../../../ui";
|
|
@@ -173,7 +174,7 @@ export function QuoteMonitorPane({ paneId, focused, width, height }: PaneProps)
|
|
|
173
174
|
useEffect(() => {
|
|
174
175
|
const coordinator = getSharedMarketDataCoordinator();
|
|
175
176
|
if (!coordinator || !liveStreaming || boardInstruments.length === 0) return;
|
|
176
|
-
void coordinator.loadQuotesBatch(boardInstruments).catch((
|
|
177
|
+
void coordinator.loadQuotesBatch(boardInstruments).catch(logFailure("ticker-detail.quote-monitor", "Warm quote monitor board failed"));
|
|
177
178
|
}, [boardInstrumentKey, liveStreaming]);
|
|
178
179
|
const { pinTicker } = usePluginTickerActions();
|
|
179
180
|
const { openPaneSettings } = usePluginAppActions();
|
|
@@ -119,6 +119,7 @@ export function getQuoteMonitorPaneSettings(
|
|
|
119
119
|
try {
|
|
120
120
|
symbols = parseTickerListInput(storedText, MAX_TICKER_LIST_SIZE);
|
|
121
121
|
} catch {
|
|
122
|
+
// Intentional ignore: unparseable saved ticker text falls back to the legacy symbol or the pane's ticker.
|
|
122
123
|
symbols = [];
|
|
123
124
|
}
|
|
124
125
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { Box } from "../../../ui";
|
|
3
3
|
import { usePaneSettingValue, useShortcut } from "../../../public/react";
|
|
4
|
-
import {
|
|
4
|
+
import { createDataTableCsv } from "../../../components/data-table/export";
|
|
5
|
+
import { dropColumnsToFit } from "../../../components/ui/table-layout";
|
|
6
|
+
import { splitDelayedStatus, DataTableStackView, KeyValueRow, PaneStatusBody, StatGrid, usePaneFooter, usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, usePaneTicker, type DataTableCell, type DataTableColumn } from "../../../components";
|
|
5
7
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
6
8
|
import type { PaneProps } from "../../../types/plugin";
|
|
7
9
|
import type { TapeQuote, TapeSnapshot, TapeTrade } from "../../../api-client/tape";
|
|
@@ -32,6 +34,12 @@ const QUOTES: DataTableColumn[] = [
|
|
|
32
34
|
{ id: "askExchange", label: "VENUE", width: 9, align: "left" },
|
|
33
35
|
{ id: "spread", label: "SPREAD BP", width: 10, align: "right" },
|
|
34
36
|
];
|
|
37
|
+
/**
|
|
38
|
+
* A narrow pane gives up the venues, then the lots, before the right edge cuts
|
|
39
|
+
* the spread: each pair together, since one side's alone would not say which
|
|
40
|
+
* side it is. The scrollbar keeps one cell, and exports keep every column.
|
|
41
|
+
*/
|
|
42
|
+
const QUOTE_DROP_ORDER = [["bidExchange", "askExchange"], ["bidSize", "askSize"]];
|
|
35
43
|
const rank = (value: number | null) => value == null ? "pctl --" : `${value.toFixed(0)} pctl`;
|
|
36
44
|
type TapeRow = { id: string; trade?: TapeTrade; quote?: TapeQuote };
|
|
37
45
|
|
|
@@ -112,13 +120,32 @@ function TimeSalesView({ width, height, focused, symbol, exchange }: PaneProps &
|
|
|
112
120
|
info: data ? [...splitDelayedStatus({ id: "feed", quoteStatus: true, parts: [{ text: `${data.feed === "sip" ? "real-time" : "15m delayed"} · ${tapeStampNewYork(data.asOf)}`, tone: "muted" }] }),
|
|
113
121
|
...(frozen ? [{ id: "paused", parts: [{ text: "paused", tone: "warning" as const }] }] : []),
|
|
114
122
|
...(!data.connected || resource.snapshotOnly ? [{ id: "snapshot", parts: [{ text: "snapshot", tone: "warning" as const }] }] : [])] : [] });
|
|
123
|
+
const quoteColumns = useMemo(() => dropColumnsToFit(QUOTES, width - 1, QUOTE_DROP_ORDER), [width]);
|
|
124
|
+
const columns = tab === "quotes" ? quoteColumns : TRADES;
|
|
115
125
|
const selectTab = (value: string) => { setTab(value); setDetail(null); setSelected(null); };
|
|
116
126
|
const { strip: tabStrip, rows: tabRows } = usePaneTabs({ tabs: TABS, activeValue: tab, onSelect: selectTab, focused: focused && !detail, dense: true });
|
|
127
|
+
const renderCell = (row: TapeRow, column: DataTableColumn): DataTableCell => {
|
|
128
|
+
if (row.trade) {
|
|
129
|
+
const trade = row.trade;
|
|
130
|
+
const tick = ticks.get(trade);
|
|
131
|
+
return { text: column.id === "time" ? tapeClockNewYork(trade.timestamp) : column.id === "price" ? tapePrice(trade.price, tradeDigits)
|
|
132
|
+
: column.id === "size" ? tapeQuantity(trade.size) : column.id === "venue" ? tapeVenue(trade.exchange)
|
|
133
|
+
: column.id === "conditions" ? tapeConditionWords(trade.conditions) : trade.tape,
|
|
134
|
+
// Price by tick, as on a tape; a block print lights the rest of its row.
|
|
135
|
+
color: laterThanPin(row) ? colors.textMuted : column.id === "price" ? tick === "up" ? colors.positive : tick === "down" ? colors.negative : colors.text
|
|
136
|
+
: trade.size >= 10_000 ? colors.warning : colors.textMuted };
|
|
137
|
+
}
|
|
138
|
+
const quote = row.quote!, spread = quoteSpread(quote);
|
|
139
|
+
return { text: column.id === "time" ? tapeClockNewYork(quote.timestamp) : column.id === "bid" ? tapePrice(quote.bid, quoteDigits) : column.id === "ask" ? tapePrice(quote.ask, quoteDigits)
|
|
140
|
+
: column.id === "bidSize" ? tapeQuantity(quote.bidSize) : column.id === "askSize" ? tapeQuantity(quote.askSize)
|
|
141
|
+
: column.id === "bidExchange" ? tapeVenue(quote.bidExchange) : column.id === "askExchange" ? tapeVenue(quote.askExchange)
|
|
142
|
+
: spread.bps == null ? "--" : spread.bps.toFixed(2), color: laterThanPin(row) ? colors.textMuted : spread.state === "normal" ? colors.text : colors.warning };
|
|
143
|
+
};
|
|
117
144
|
if (!data && isCloudSessionRequired(resource.error)) return <SignInWall placement="time-sales-signin" action="view time and sales" needsVerification={session.needsVerification} />;
|
|
118
145
|
return <Box width={width} height={height} flexDirection="column">
|
|
119
146
|
{tabStrip}
|
|
120
147
|
<PaneStatusBody loading={resource.loading && !data} error={!data ? resource.error : null} empty={!!data && !rows.length} subject={tab === "quotes" ? "NBBO observations" : "trade observations"}>
|
|
121
|
-
{data && stats ? <DataTableStackView<TapeRow> columns={
|
|
148
|
+
{data && stats ? <DataTableStackView<TapeRow> columns={columns} items={rows}
|
|
122
149
|
focused={focused} rootWidth={width} rootHeight={Math.max(3, height - tabRows)}
|
|
123
150
|
selection={{ kind: "id", selectedId: selected, getId: (row) => row.id, onChange: setSelected }}
|
|
124
151
|
onActivate={(row) => { setPaused({ data, epoch: resource.epoch }); setDetail({ row, epoch: resource.epoch }); }} getItemKey={(row) => row.id}
|
|
@@ -136,23 +163,8 @@ function TimeSalesView({ width, height, focused, symbol, exchange }: PaneProps &
|
|
|
136
163
|
]),
|
|
137
164
|
...(data.session.high != null && data.session.low != null ? [{ id: "session", label: "Session", value: `${tapePrice(data.session.low, sessionDigits)} to ${tapePrice(data.session.high, sessionDigits)}` }] : []),
|
|
138
165
|
]} />}
|
|
139
|
-
renderCell={(
|
|
140
|
-
|
|
141
|
-
const trade = row.trade;
|
|
142
|
-
const tick = ticks.get(trade);
|
|
143
|
-
return { text: column.id === "time" ? tapeClockNewYork(trade.timestamp) : column.id === "price" ? tapePrice(trade.price, tradeDigits)
|
|
144
|
-
: column.id === "size" ? tapeQuantity(trade.size) : column.id === "venue" ? tapeVenue(trade.exchange)
|
|
145
|
-
: column.id === "conditions" ? tapeConditionWords(trade.conditions) : trade.tape,
|
|
146
|
-
// Price by tick, as on a tape; a block print lights the rest of its row.
|
|
147
|
-
color: laterThanPin(row) ? colors.textMuted : column.id === "price" ? tick === "up" ? colors.positive : tick === "down" ? colors.negative : colors.text
|
|
148
|
-
: trade.size >= 10_000 ? colors.warning : colors.textMuted };
|
|
149
|
-
}
|
|
150
|
-
const quote = row.quote!, spread = quoteSpread(quote);
|
|
151
|
-
return { text: column.id === "time" ? tapeClockNewYork(quote.timestamp) : column.id === "bid" ? tapePrice(quote.bid, quoteDigits) : column.id === "ask" ? tapePrice(quote.ask, quoteDigits)
|
|
152
|
-
: column.id === "bidSize" ? tapeQuantity(quote.bidSize) : column.id === "askSize" ? tapeQuantity(quote.askSize)
|
|
153
|
-
: column.id === "bidExchange" ? tapeVenue(quote.bidExchange) : column.id === "askExchange" ? tapeVenue(quote.askExchange)
|
|
154
|
-
: spread.bps == null ? "--" : spread.bps.toFixed(2), color: laterThanPin(row) ? colors.textMuted : spread.state === "normal" ? colors.text : colors.warning };
|
|
155
|
-
}} emptyStateTitle="No observations in this window." /> : null}
|
|
166
|
+
renderCell={renderCell} exportCsv={() => createDataTableCsv({ columns: tab === "quotes" ? QUOTES : TRADES, items: rows, renderCell })}
|
|
167
|
+
emptyStateTitle="No observations in this window." /> : null}
|
|
156
168
|
</PaneStatusBody>
|
|
157
169
|
</Box>;
|
|
158
170
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { httpFetch } from "../../../utils/http-transport";
|
|
2
2
|
import { numericOrNull, scalarTextOrNull } from "../../../utils/guards";
|
|
3
|
-
import type
|
|
3
|
+
import { cashLineLabel, type CashAccount, type DebtDay, type OperatingCash, type TgaClose } from "./model";
|
|
4
4
|
|
|
5
5
|
const CASH_URL = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v1/accounting/dts/operating_cash_balance?sort=-record_date&page[size]=40";
|
|
6
|
+
// One line of the statement, every day since it took its present form in 2022: about a thousand closes.
|
|
7
|
+
const TGA_CLOSING_ACCOUNT = "Treasury General Account (TGA) Closing Balance";
|
|
8
|
+
const TGA_HISTORY_URL = `https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v1/accounting/dts/operating_cash_balance?${new URLSearchParams({
|
|
9
|
+
filter: `account_type:eq:${TGA_CLOSING_ACCOUNT}`,
|
|
10
|
+
fields: "record_date,open_today_bal",
|
|
11
|
+
sort: "-record_date",
|
|
12
|
+
"page[size]": "3000",
|
|
13
|
+
})}`;
|
|
6
14
|
const DEBT_URL = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v2/accounting/od/debt_to_penny?sort=-record_date&page[size]=20";
|
|
7
15
|
const FETCH_TIMEOUT_MS = 15_000;
|
|
8
16
|
const DEBT_DAYS = 20;
|
|
@@ -10,6 +18,9 @@ const DEBT_DAYS = 20;
|
|
|
10
18
|
export interface TreasuryDaily {
|
|
11
19
|
cash: OperatingCash;
|
|
12
20
|
debt: DebtDay[];
|
|
21
|
+
/** The Treasury General Account's closing balance each day, oldest first. Empty when it could not be read, with the reason. */
|
|
22
|
+
tga: TgaClose[];
|
|
23
|
+
tgaError?: string;
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
function recordDate(value: unknown): string | null {
|
|
@@ -39,7 +50,7 @@ function parseOperatingCash(payload: unknown): OperatingCash {
|
|
|
39
50
|
parsed.push({
|
|
40
51
|
id: `${date}:${line ?? account}`,
|
|
41
52
|
date,
|
|
42
|
-
account,
|
|
53
|
+
account: cashLineLabel(account),
|
|
43
54
|
// Since the 2022 statement format the day's figures are in open_today_bal
|
|
44
55
|
// and close_today_bal is always "null".
|
|
45
56
|
today: numericOrNull(row.open_today_bal),
|
|
@@ -60,6 +71,17 @@ function parseOperatingCash(payload: unknown): OperatingCash {
|
|
|
60
71
|
};
|
|
61
72
|
}
|
|
62
73
|
|
|
74
|
+
function parseTgaHistory(payload: unknown): TgaClose[] {
|
|
75
|
+
const byDate = new Map<string, number>();
|
|
76
|
+
for (const row of rowsOf(payload)) {
|
|
77
|
+
const date = recordDate(row.record_date);
|
|
78
|
+
// The closing balance is sent in open_today_bal, as the statement's other lines are.
|
|
79
|
+
const millions = numericOrNull(row.open_today_bal);
|
|
80
|
+
if (date && millions != null && !byDate.has(date)) byDate.set(date, millions);
|
|
81
|
+
}
|
|
82
|
+
return [...byDate].map(([date, millions]) => ({ date, millions })).sort((a, b) => a.date.localeCompare(b.date));
|
|
83
|
+
}
|
|
84
|
+
|
|
63
85
|
function parseDebtToPenny(payload: unknown): DebtDay[] {
|
|
64
86
|
const byDate = new Map<string, DebtDay>();
|
|
65
87
|
for (const row of rowsOf(payload)) {
|
|
@@ -87,10 +109,24 @@ async function readJson(url: string, label: string, signal?: AbortSignal): Promi
|
|
|
87
109
|
return response.json() as Promise<unknown>;
|
|
88
110
|
}
|
|
89
111
|
|
|
90
|
-
|
|
91
|
-
|
|
112
|
+
/**
|
|
113
|
+
* The latest day's cash lines and the last 20 days of debt, with the account's
|
|
114
|
+
* closing balance history unless `history` is off (a report that does not draw it).
|
|
115
|
+
* A history that cannot be read leaves the figures as they are.
|
|
116
|
+
*/
|
|
117
|
+
export async function fetchTreasuryDaily(signal?: AbortSignal, { history = true }: { history?: boolean } = {}): Promise<TreasuryDaily> {
|
|
118
|
+
const [cash, debt, tga] = await Promise.all([
|
|
92
119
|
readJson(CASH_URL, "Treasury cash", signal),
|
|
93
120
|
readJson(DEBT_URL, "Treasury debt", signal),
|
|
121
|
+
history ? readJson(TGA_HISTORY_URL, "Treasury account history", signal).then(parseTgaHistory).then(
|
|
122
|
+
(closes) => ({ closes }),
|
|
123
|
+
(error: unknown) => ({ closes: [] as TgaClose[], error: error instanceof Error ? error.message : String(error) }),
|
|
124
|
+
) : { closes: [] as TgaClose[] },
|
|
94
125
|
]);
|
|
95
|
-
return {
|
|
126
|
+
return {
|
|
127
|
+
cash: parseOperatingCash(cash),
|
|
128
|
+
debt: parseDebtToPenny(debt),
|
|
129
|
+
tga: tga.closes,
|
|
130
|
+
...("error" in tga ? { tgaError: tga.error } : {}),
|
|
131
|
+
};
|
|
96
132
|
}
|
|
@@ -3,7 +3,7 @@ import { fetchTreasuryDaily } from "./client";
|
|
|
3
3
|
import { formatBillions, formatTrillions } from "./model";
|
|
4
4
|
|
|
5
5
|
const CASH_COLUMNS: HeadlessPaneColumn[] = [
|
|
6
|
-
{ key: "account", header: "Account" },
|
|
6
|
+
{ key: "account", header: "Treasury General Account" },
|
|
7
7
|
{ key: "today", header: "Today $bn", align: "right", description: "The day's figure, billions of dollars", format: formatBillions },
|
|
8
8
|
{ key: "month", header: "This month $bn", align: "right", description: "Month to date, billions of dollars", format: formatBillions },
|
|
9
9
|
];
|
|
@@ -32,7 +32,7 @@ export const treasuryDailyHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
32
32
|
describe: "Treasury operating cash and public debt",
|
|
33
33
|
freshness: { source: "US Treasury", status: "not-a-feed", basis: "daily statement" },
|
|
34
34
|
async load(_args, ctx) {
|
|
35
|
-
const { cash, debt } = await fetchTreasuryDaily(ctx.signal);
|
|
35
|
+
const { cash, debt } = await fetchTreasuryDaily(ctx.signal, { history: false });
|
|
36
36
|
return {
|
|
37
37
|
complete: cash.rows.length > 0 && debt.length > 0,
|
|
38
38
|
sections: [
|
|
@@ -8,7 +8,7 @@ export const treasuryDailyModule: PluginModule = {
|
|
|
8
8
|
id: "treasury-daily-pane",
|
|
9
9
|
paneId: TREASURY_DAILY_PANE_ID,
|
|
10
10
|
label: "Treasury Cash and Debt",
|
|
11
|
-
description: "Treasury operating cash for the latest day, and debt held by the public for the last 20 business days.",
|
|
11
|
+
description: "Treasury operating cash for the latest day with the Treasury General Account's closing balance history, and debt held by the public for the last 20 business days.",
|
|
12
12
|
keywords: ["treasury", "dts", "debt", "cash", "tga", "debt to the penny", "operating cash"],
|
|
13
13
|
shortcut: { prefix: "DTS" },
|
|
14
14
|
headless: treasuryDailyHeadless,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DataTableColumn } from "../../../components";
|
|
2
|
+
import { formatNumber } from "../../../utils/format";
|
|
2
3
|
|
|
3
4
|
export const TREASURY_DAILY_PANE_ID = "treasury-daily";
|
|
4
5
|
|
|
@@ -23,6 +24,51 @@ export interface OperatingCash {
|
|
|
23
24
|
rows: CashAccount[];
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
/** One day's Treasury General Account closing balance, millions of dollars. */
|
|
28
|
+
export interface TgaClose {
|
|
29
|
+
date: string;
|
|
30
|
+
millions: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The longest stretch of closing balances the Cash tab charts, and the ranges it offers. */
|
|
34
|
+
export type TgaRange = "3M" | "6M" | "1Y" | "ALL";
|
|
35
|
+
export const TGA_RANGES: ReadonlyArray<{ value: TgaRange; label: string; hint: string; months: number | null }> = [
|
|
36
|
+
{ value: "3M", label: "3M", hint: "1", months: 3 },
|
|
37
|
+
{ value: "6M", label: "6M", hint: "2", months: 6 },
|
|
38
|
+
{ value: "1Y", label: "1Y", hint: "3", months: 12 },
|
|
39
|
+
{ value: "ALL", label: "All", hint: "4", months: null },
|
|
40
|
+
];
|
|
41
|
+
const DEFAULT_TGA_RANGE: TgaRange = "1Y";
|
|
42
|
+
|
|
43
|
+
export function tgaRange(value: unknown): TgaRange {
|
|
44
|
+
return TGA_RANGES.find((range) => range.value === value)?.value ?? DEFAULT_TGA_RANGE;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The closes of the last `range`, counted back from the newest one, oldest first. */
|
|
48
|
+
export function tgaClosesInRange(closes: readonly TgaClose[], range: TgaRange): TgaClose[] {
|
|
49
|
+
const months = TGA_RANGES.find((entry) => entry.value === range)?.months ?? null;
|
|
50
|
+
const last = closes.at(-1);
|
|
51
|
+
if (months == null || !last) return [...closes];
|
|
52
|
+
const start = new Date(`${last.date}T00:00:00Z`);
|
|
53
|
+
start.setUTCMonth(start.getUTCMonth() - months);
|
|
54
|
+
const from = start.toISOString().slice(0, 10);
|
|
55
|
+
return closes.filter((close) => close.date >= from);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* What the Treasury's statement calls a line of the Treasury General Account,
|
|
60
|
+
* said plainly: `Treasury General Account (TGA) Opening Balance` is the
|
|
61
|
+
* opening balance, `Total TGA Withdrawals (Table II) (-)` the withdrawals. A
|
|
62
|
+
* line the statement adds later keeps its own words, less the table reference.
|
|
63
|
+
*/
|
|
64
|
+
export function cashLineLabel(account: string): string {
|
|
65
|
+
if (/^Treasury General Account \(TGA\) Opening Balance$/i.test(account)) return "Opening balance";
|
|
66
|
+
if (/^Treasury General Account \(TGA\) Closing Balance$/i.test(account)) return "Closing balance";
|
|
67
|
+
if (/^Total TGA Deposits\b/i.test(account)) return "Deposits";
|
|
68
|
+
if (/^Total TGA Withdrawals\b/i.test(account)) return "Withdrawals";
|
|
69
|
+
return account.replace(/\s*\(Table [IVX]+\)/g, "").replace(/\s*\(-\)$/, "").trim();
|
|
70
|
+
}
|
|
71
|
+
|
|
26
72
|
export interface DebtDay {
|
|
27
73
|
id: string;
|
|
28
74
|
date: string;
|
|
@@ -40,7 +86,7 @@ export type CashColumn = DataTableColumn & { id: CashColumnId };
|
|
|
40
86
|
export type DebtColumn = DataTableColumn & { id: DebtColumnId };
|
|
41
87
|
|
|
42
88
|
export const CASH_COLUMNS: CashColumn[] = [
|
|
43
|
-
{ id: "account", label: "Account", width:
|
|
89
|
+
{ id: "account", label: "Treasury General Account", width: 26, align: "left", flexGrow: 1 },
|
|
44
90
|
{ id: "today", label: "Today $bn", width: 12, align: "right" },
|
|
45
91
|
{ id: "month", label: "This month $bn", width: 15, align: "right" },
|
|
46
92
|
];
|
|
@@ -52,10 +98,11 @@ export const DEBT_COLUMNS: DebtColumn[] = [
|
|
|
52
98
|
{ id: "total", label: "Total $tn", width: 12, align: "right" },
|
|
53
99
|
];
|
|
54
100
|
|
|
101
|
+
/** Two decimals with thousands separators, as every money table writes a large figure: `2,594.63`. */
|
|
55
102
|
function scaled(value: unknown, divisor: number): string {
|
|
56
103
|
if (typeof value !== "number" || !Number.isFinite(value)) return "--";
|
|
57
|
-
const
|
|
58
|
-
return
|
|
104
|
+
const rounded = Number((value / divisor).toFixed(2));
|
|
105
|
+
return formatNumber(rounded === 0 ? 0 : rounded, 2);
|
|
59
106
|
}
|
|
60
107
|
|
|
61
108
|
/** Millions of dollars, drawn as billions. */
|
|
@@ -1,33 +1,52 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from "react";
|
|
2
2
|
import {
|
|
3
|
+
ChartTableHeader,
|
|
3
4
|
DataTableView,
|
|
4
5
|
PaneStatusBody,
|
|
6
|
+
QueryBar,
|
|
7
|
+
usePaneNoticeFooter,
|
|
5
8
|
usePaneStatusFooter,
|
|
6
9
|
usePaneTabs,
|
|
10
|
+
type ChartTableChart,
|
|
7
11
|
type DataTableCell,
|
|
12
|
+
type DataTableKeyEvent,
|
|
8
13
|
} from "../../../components";
|
|
9
|
-
import {
|
|
14
|
+
import { spanAxisFormatter } from "../../../components/chart-table";
|
|
15
|
+
import { useChartViewReset } from "../../../components/chart/core/view-reset";
|
|
16
|
+
import { scalarPoint, staticSeries } from "../../../components/chart/static/series";
|
|
17
|
+
import { handleRefreshKey, usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
10
18
|
import { useAsyncResource, useAutoRefresh, usePluginPaneState } from "../../../public/react";
|
|
11
19
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
12
20
|
import type { PaneProps } from "../../../types/plugin";
|
|
13
21
|
import { Box } from "../../../ui";
|
|
22
|
+
import { formatNumber } from "../../../utils/format";
|
|
23
|
+
import { isPlainKeyboardEvent } from "../../../utils/keyboard";
|
|
14
24
|
import { fetchTreasuryDaily } from "./client";
|
|
15
25
|
import {
|
|
16
26
|
CASH_COLUMNS,
|
|
17
27
|
DEBT_COLUMNS,
|
|
28
|
+
TGA_RANGES,
|
|
18
29
|
TREASURY_DAILY_PANE_ID,
|
|
19
30
|
TREASURY_TABS,
|
|
20
31
|
formatBillions,
|
|
21
32
|
formatTrillions,
|
|
33
|
+
tgaClosesInRange,
|
|
34
|
+
tgaRange,
|
|
22
35
|
type CashAccount,
|
|
23
36
|
type CashColumn,
|
|
24
37
|
type DebtColumn,
|
|
25
38
|
type DebtDay,
|
|
39
|
+
type TgaRange,
|
|
26
40
|
type TreasuryTab,
|
|
27
41
|
} from "./model";
|
|
28
42
|
|
|
29
43
|
const EMPTY_CASH: CashAccount[] = [];
|
|
30
44
|
const EMPTY_DEBT: DebtDay[] = [];
|
|
45
|
+
const RANGE_OPTIONS = TGA_RANGES.map(({ value, label, hint }) => ({ value, label, hint }));
|
|
46
|
+
const TGA_SERIES_LABEL = "TGA closing balance $bn";
|
|
47
|
+
/** Ticks in billions, with thousands separators and the decimals the plotted range needs. */
|
|
48
|
+
const formatBillionsAxis = spanAxisFormatter((value, digits) => formatNumber(value, digits));
|
|
49
|
+
const formatBillionsLevel = (value: number) => formatNumber(value, 2);
|
|
31
50
|
|
|
32
51
|
function scaledCell(value: number | null, text: string, muted: string): DataTableCell {
|
|
33
52
|
return value == null ? { text, value, color: muted } : { text, value };
|
|
@@ -44,6 +63,9 @@ export function TreasuryDailyPane({ width, height, focused }: PaneProps) {
|
|
|
44
63
|
const debtRows = data?.debt ?? EMPTY_DEBT;
|
|
45
64
|
const activeRows = tab === "cash" ? cashRows : debtRows;
|
|
46
65
|
const recordDate = tab === "cash" ? data?.cash.recordDate ?? null : debtRows[0]?.date ?? null;
|
|
66
|
+
const [storedRange, setStoredRange] = usePluginPaneState<string>("range", "1Y");
|
|
67
|
+
const range = tgaRange(storedRange);
|
|
68
|
+
const resetChartView = useChartViewReset();
|
|
47
69
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
48
70
|
const resolvedId = activeRows.some((row) => row.id === selectedId) ? selectedId : activeRows[0]?.id ?? null;
|
|
49
71
|
const info = useMemo(
|
|
@@ -58,13 +80,46 @@ export function TreasuryDailyPane({ width, height, focused }: PaneProps) {
|
|
|
58
80
|
dense: true,
|
|
59
81
|
});
|
|
60
82
|
useAutoRefresh(resource.updatedAt, resource.load);
|
|
61
|
-
|
|
83
|
+
const refresh = useCallback(() => void resource.reload(), [resource.reload]);
|
|
84
|
+
usePaneRefreshKey(refresh, { focused });
|
|
62
85
|
usePaneStatusFooter({
|
|
63
86
|
registrationId: TREASURY_DAILY_PANE_ID,
|
|
64
87
|
loading: resource.loading,
|
|
65
88
|
error: resource.error,
|
|
66
89
|
info,
|
|
67
90
|
});
|
|
91
|
+
usePaneNoticeFooter({
|
|
92
|
+
registrationId: `${TREASURY_DAILY_PANE_ID}:history`,
|
|
93
|
+
focused: focused && tab === "cash",
|
|
94
|
+
notices: data?.tgaError ? [`Closing balance history is unavailable: ${data.tgaError}`] : [],
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// The closing balance the table's last line states for the day, drawn over the range chosen.
|
|
98
|
+
const series = useMemo(() => {
|
|
99
|
+
const closes = tgaClosesInRange(data?.tga ?? [], range);
|
|
100
|
+
return closes.length < 2 ? [] : [staticSeries(
|
|
101
|
+
closes.map((close) => scalarPoint(new Date(`${close.date}T00:00:00Z`), close.millions / 1_000)),
|
|
102
|
+
{ id: "tga-closing-balance", label: TGA_SERIES_LABEL, color: colors.positive, calendarSpaced: true },
|
|
103
|
+
)];
|
|
104
|
+
}, [colors.positive, data?.tga, range]);
|
|
105
|
+
const chart: ChartTableChart | null = data?.tgaError ? null : {
|
|
106
|
+
series,
|
|
107
|
+
formatValue: formatBillionsLevel,
|
|
108
|
+
formatAxisValue: formatBillionsAxis,
|
|
109
|
+
remoteKind: "treasury-general-account",
|
|
110
|
+
empty: "No closing balance history.",
|
|
111
|
+
};
|
|
112
|
+
// The number keys pick the range, as the bar's hints say; picking the one showing sends the chart back to it.
|
|
113
|
+
const handleKeyDown = useCallback((event: DataTableKeyEvent) => {
|
|
114
|
+
if (handleRefreshKey(event, refresh, { stopPropagation: true })) return true;
|
|
115
|
+
if (!isPlainKeyboardEvent(event)) return false;
|
|
116
|
+
const picked = TGA_RANGES.find((option) => option.hint === event.name);
|
|
117
|
+
if (!picked) return false;
|
|
118
|
+
event.preventDefault?.();
|
|
119
|
+
if (picked.value === range) resetChartView();
|
|
120
|
+
else setStoredRange(picked.value);
|
|
121
|
+
return true;
|
|
122
|
+
}, [range, refresh, resetChartView, setStoredRange]);
|
|
68
123
|
|
|
69
124
|
const bodyHeight = Math.max(1, height - tabRows);
|
|
70
125
|
const renderCash = useCallback((row: CashAccount, column: CashColumn): DataTableCell => {
|
|
@@ -91,6 +146,30 @@ export function TreasuryDailyPane({ width, height, focused }: PaneProps) {
|
|
|
91
146
|
focused={focused}
|
|
92
147
|
rootWidth={width}
|
|
93
148
|
rootHeight={bodyHeight}
|
|
149
|
+
rootBefore={chart ? (
|
|
150
|
+
<ChartTableHeader
|
|
151
|
+
width={width}
|
|
152
|
+
height={bodyHeight}
|
|
153
|
+
tableRows={cashRows.length}
|
|
154
|
+
tableColumns={CASH_COLUMNS}
|
|
155
|
+
query={(
|
|
156
|
+
<QueryBar
|
|
157
|
+
width={width}
|
|
158
|
+
filters={[{
|
|
159
|
+
id: "range",
|
|
160
|
+
label: "Range",
|
|
161
|
+
inline: true,
|
|
162
|
+
chartRange: true,
|
|
163
|
+
value: range,
|
|
164
|
+
options: RANGE_OPTIONS,
|
|
165
|
+
onChange: (value: string) => setStoredRange(tgaRange(value) satisfies TgaRange),
|
|
166
|
+
}]}
|
|
167
|
+
/>
|
|
168
|
+
)}
|
|
169
|
+
chart={chart}
|
|
170
|
+
/>
|
|
171
|
+
) : undefined}
|
|
172
|
+
onRootKeyDown={handleKeyDown}
|
|
94
173
|
columns={CASH_COLUMNS}
|
|
95
174
|
items={cashRows}
|
|
96
175
|
sortColumnId={null}
|
|
@@ -4,10 +4,15 @@ import type { ProjectedChartPoint } from "../../../components/chart/core/data";
|
|
|
4
4
|
import { resolveChartPalette } from "../../../components/chart/core/palette";
|
|
5
5
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
6
6
|
import { Box, ScrollBox, Text } from "../../../ui";
|
|
7
|
+
import { formatStrikeLabel } from "../../../utils/options";
|
|
7
8
|
import { evaluateSmile, optionDelta } from "../shared/volatility";
|
|
8
|
-
import { buildSurfaceGrid, evaluateSurfaceSmile, type SurfaceExpiry, type SurfaceSnapshot } from "./model";
|
|
9
|
+
import { buildSurfaceGrid, deltaBasis, evaluateSurfaceSmile, type SurfaceExpiry, type SurfaceSnapshot } from "./model";
|
|
9
10
|
import { expiryLabel, formatPrice } from "./tables";
|
|
10
11
|
|
|
12
|
+
// A strike tick reads as OMON writes strikes; the moneyness and delta axes stay whole numbers.
|
|
13
|
+
const tickLabel = (value: number, axis: string) => axis === "strike" ? formatStrikeLabel(Number(value.toFixed(2)))
|
|
14
|
+
: `${value.toFixed(0)}${axis === "spot" || axis === "forward" ? "%" : ""}`;
|
|
15
|
+
|
|
11
16
|
const point = (x: number, y: number): ProjectedChartPoint => ({ date: new Date(Math.round(x * 1_000_000)), open: y, high: y, low: y, close: y, volume: 0 });
|
|
12
17
|
|
|
13
18
|
export function SmileChart({ snapshot, expiry, overlay, axis, width, height, focused = false }: {
|
|
@@ -25,8 +30,8 @@ export function SmileChart({ snapshot, expiry, overlay, axis, width, height, foc
|
|
|
25
30
|
if (axis === "strike") return strike;
|
|
26
31
|
if (axis === "delta") {
|
|
27
32
|
const volatility = evaluateSmile(expiry.fit!, Math.log(strike / expiry.forward!));
|
|
28
|
-
const delta = volatility == null ? null : optionDelta({
|
|
29
|
-
|
|
33
|
+
const delta = volatility == null ? null : optionDelta({ ...deltaBasis(expiry.rateMethod, snapshot.spot, expiry.forward!,
|
|
34
|
+
expiry.rate ?? 0, expiry.dividendYield ?? 0), years: expiry.years, volatility }, strike, "call");
|
|
30
35
|
return delta == null ? NaN : (1 - delta) * 100;
|
|
31
36
|
}
|
|
32
37
|
return strike / (axis === "forward" ? expiry.forward! : snapshot.spot) * 100;
|
|
@@ -57,7 +62,7 @@ export function SmileChart({ snapshot, expiry, overlay, axis, width, height, foc
|
|
|
57
62
|
}
|
|
58
63
|
const left = observations[0]?.x ?? 0, right = observations.at(-1)?.x ?? 1;
|
|
59
64
|
return { points: observations.map((entry) => point(entry.x, entry.iv * 100)), overlays,
|
|
60
|
-
ticks: Array.from({ length: 5 }, (_, i) => ({ ratio: i / 4, label:
|
|
65
|
+
ticks: Array.from({ length: 5 }, (_, i) => ({ ratio: i / 4, label: tickLabel(left + (right - left) * i / 4, axis) })),
|
|
61
66
|
left, right };
|
|
62
67
|
}, [axis, colors, expiry, overlay, snapshot]);
|
|
63
68
|
if (!model) return <EmptyState title="Smile unavailable." hint="No clean quoted smile for this expiry." />;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
|
+
import { createTtlCache } from "../../../data/ttl-cache";
|
|
2
3
|
import { getSharedMarketDataCoordinator, MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
|
|
3
4
|
import type { InstrumentRef, OptionsRequest } from "../../../market-data/request-types";
|
|
4
5
|
import type { QueryEntry } from "../../../market-data/result-types";
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
} from "./model";
|
|
13
14
|
import { abortable, abortError } from "../../../utils/async-deadline";
|
|
14
15
|
import { errorMessage } from "../../../utils/errors";
|
|
15
|
-
import { expiredExpiryText, findListedExpiry, isLiveOptionExpiry, liveOptionExpiries, unlistedExpiryText } from "../../../utils/option-expiry";
|
|
16
|
+
import { expiredExpiryText, findListedExpiry, isLiveOptionExpiry, liveOptionExpiries, optionExpiryVenue, unlistedExpiryText } from "../../../utils/option-expiry";
|
|
16
17
|
import { optionModelSpot } from "../options/model-spot";
|
|
17
18
|
|
|
18
19
|
const DEFAULT_SURFACE_EXPIRY_LIMIT = 18;
|
|
@@ -73,19 +74,8 @@ export function withReusedYieldCurve(
|
|
|
73
74
|
dependencies: SurfaceLoaderDependencies,
|
|
74
75
|
reuseMs = TREASURY_CURVE_REUSE_MS,
|
|
75
76
|
): SurfaceLoaderDependencies {
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
...dependencies,
|
|
79
|
-
loadYieldCurve: () => {
|
|
80
|
-
const now = Date.now();
|
|
81
|
-
if (held && now - held.at < reuseMs) return held.curve;
|
|
82
|
-
const curve = dependencies.loadYieldCurve();
|
|
83
|
-
const entry = { at: now, curve };
|
|
84
|
-
held = entry;
|
|
85
|
-
curve.catch(() => { if (held === entry) held = null; });
|
|
86
|
-
return curve;
|
|
87
|
-
},
|
|
88
|
-
};
|
|
77
|
+
const curves = createTtlCache<YieldPoint[]>({ ttlMs: reuseMs });
|
|
78
|
+
return { ...dependencies, loadYieldCurve: () => curves.load("curve", () => dependencies.loadYieldCurve()) };
|
|
89
79
|
}
|
|
90
80
|
|
|
91
81
|
export interface SurfaceLoadRequest {
|
|
@@ -197,6 +187,8 @@ export async function loadVolatilitySurface(
|
|
|
197
187
|
publish();
|
|
198
188
|
});
|
|
199
189
|
|
|
190
|
+
// Expiries close on the listing's clock: a coin's options (Deribit) settle at 08:00 UTC on any day.
|
|
191
|
+
const expiryVenue = optionExpiryVenue(request.instrument.symbol, request.instrument.exchange);
|
|
200
192
|
try {
|
|
201
193
|
const initial = await abortable(dependencies.loadOptions({ instrument: request.instrument },
|
|
202
194
|
{ forceRefresh: request.expiries ? false : request.forceRefresh }), request.signal, CANCELLED);
|
|
@@ -217,7 +209,7 @@ export async function loadVolatilitySurface(
|
|
|
217
209
|
catalogueError = initial.error?.message ?? null;
|
|
218
210
|
catalogueReason = initial.error?.reasonCode;
|
|
219
211
|
if (!chain) catalogueError ??= "Options expiry catalogue unavailable";
|
|
220
|
-
catalogue = liveOptionExpiries(chain?.expirationDates ?? [], now,
|
|
212
|
+
catalogue = liveOptionExpiries(chain?.expirationDates ?? [], now, expiryVenue);
|
|
221
213
|
// A typed date is that day's UTC midnight; the catalogue's stamp for the
|
|
222
214
|
// same expiry is matched by calendar date, as OMON matches it.
|
|
223
215
|
const required = [...new Set([...(request.requiredExpiries ?? []), ...(request.expiries ?? [])]
|
|
@@ -228,8 +220,8 @@ export async function loadVolatilitySurface(
|
|
|
228
220
|
...required.filter((expiration) => listed.has(expiration))])].sort((a, b) => a - b);
|
|
229
221
|
// An expiry whose last session has closed is named as expired, with the nearest live one to pick.
|
|
230
222
|
requiredFailures = required.filter((expiration) => !listed.has(expiration)).map((expiration) => ({
|
|
231
|
-
expiration, message: isLiveOptionExpiry(expiration, now,
|
|
232
|
-
: expiredExpiryText(expiration, catalogue, now, { exchange:
|
|
223
|
+
expiration, message: isLiveOptionExpiry(expiration, now, expiryVenue) ? unlistedExpiryText(expiration, catalogue)
|
|
224
|
+
: expiredExpiryText(expiration, catalogue, now, { exchange: expiryVenue }),
|
|
233
225
|
}));
|
|
234
226
|
if (chain && catalogue.length === 0) catalogueError ??= "No unexpired option expiries available";
|
|
235
227
|
} catch (error) {
|