@gloom-sh/terminal 0.18.5 → 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/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +140 -7
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +17 -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 +11 -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 +1 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/types.ts +70 -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/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 +72 -26
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +61 -12
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +55 -34
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +8 -0
- package/src/cli/glossary.ts +25 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +180 -52
- 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 +86 -3
- package/src/cli/pane-functions/function-help.ts +13 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +49 -14
- 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/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/pane-templates/items.ts +3 -2
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- 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 +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/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/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 +71 -137
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/market-data/depositary-pair.ts +2 -2
- 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/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/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +336 -77
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- 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 +24 -6
- 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/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/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- 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/pane-links/commands.ts +7 -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/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/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +6 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +36 -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 +1 -0
- 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 +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/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/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 +2 -1
- 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 +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 +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/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- 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 +7 -5
- 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/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 +27 -3
- 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 +2 -1
- package/src/plugins/builtin/series-inventory.ts +2 -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/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/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/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-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/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 +2 -2
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- 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/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 +12 -0
- 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/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -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/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/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/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.ts +44 -3
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/terminal-codes.ts +19 -0
- 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/financials.ts +32 -1
- package/src/types/headless.ts +21 -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 +44 -0
- 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/price-history.ts +7 -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
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { logFailure } from "../../../../utils/log-failure";
|
|
1
2
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
3
|
import { TextAttributes } from "../../../../ui";
|
|
3
4
|
import {
|
|
4
5
|
DataTableView,
|
|
5
6
|
QueryBar,
|
|
7
|
+
StatGrid,
|
|
6
8
|
loadingText,
|
|
7
9
|
unavailableText,
|
|
8
10
|
usePaneNoticeFooter,
|
|
@@ -12,11 +14,13 @@ import {
|
|
|
12
14
|
type DataTableColumn,
|
|
13
15
|
type PaneFooterSegment,
|
|
14
16
|
type PaneHint,
|
|
17
|
+
type StatItem,
|
|
15
18
|
} from "../../../../components";
|
|
16
19
|
import { usePaneRefreshKey } from "../../../../components/data-table/table-pane";
|
|
17
20
|
import { CHART_RESOLUTIONS } from "../../../../time-series/range";
|
|
18
21
|
import {
|
|
19
22
|
getChartResolutionLabel,
|
|
23
|
+
isIntradayResolution,
|
|
20
24
|
type ChartResolutionSupport,
|
|
21
25
|
type ManualChartResolution,
|
|
22
26
|
} from "../../../../time-series/resolution";
|
|
@@ -25,7 +29,7 @@ import { cleanFloat32Price, historyPriceDecimals } from "../../../../cli/history
|
|
|
25
29
|
import { formatPriceObservation, quoteFormatOptions } from "../../../../market-data/market/format";
|
|
26
30
|
import { usePaneTicker } from "../../../../state/app/context";
|
|
27
31
|
import type { PaneProps } from "../../../../types/plugin";
|
|
28
|
-
import { colors,
|
|
32
|
+
import { colors, shownPriceColor } from "../../../../theme/colors";
|
|
29
33
|
import { formatPercent } from "../../../../utils/format";
|
|
30
34
|
import { publicTickerKey } from "../../../../utils/exchanges";
|
|
31
35
|
import { priceHistoryIntegrityNotice } from "../../../../utils/price-history-integrity";
|
|
@@ -39,9 +43,12 @@ import {
|
|
|
39
43
|
effectiveReturnResolution,
|
|
40
44
|
isTodayReturnRow,
|
|
41
45
|
returnBaseCloseDay,
|
|
46
|
+
returnTimeHeader,
|
|
47
|
+
returnTimeText,
|
|
42
48
|
RETURN_RANGES,
|
|
43
49
|
type ReturnRange,
|
|
44
50
|
type ReturnRow,
|
|
51
|
+
summarizeReturns,
|
|
45
52
|
} from "./returns-model";
|
|
46
53
|
import { loadReturnSeries, type ReturnSeries } from "./returns-history";
|
|
47
54
|
import { formingHistoryBar, formingHistoryBarText, historyBarDate } from "./history-bars";
|
|
@@ -64,12 +71,6 @@ const COLUMNS: ReturnColumn[] = [
|
|
|
64
71
|
{ id: "cumulativePercent", label: "TOTAL %", width: 9, align: "right", flexGrow: 1 },
|
|
65
72
|
];
|
|
66
73
|
|
|
67
|
-
function formatDateTime(date: Date): string {
|
|
68
|
-
const iso = date.toISOString();
|
|
69
|
-
const hasTime = date.getUTCHours() !== 0 || date.getUTCMinutes() !== 0 || date.getUTCSeconds() !== 0;
|
|
70
|
-
return hasTime ? iso.slice(0, 16).replace("T", " ") : iso.slice(0, 10);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
74
|
/** Every price and dollar change at one decimal count, so the columns line up. */
|
|
74
75
|
function returnPriceDecimals(rows: readonly ReturnRow[], assetCategory?: string): number {
|
|
75
76
|
return historyPriceDecimals(rows.flatMap((row) => row.price === null ? [] : [{
|
|
@@ -95,6 +96,12 @@ function formatMaybePercent(value: number | null): string {
|
|
|
95
96
|
return value === null ? "-" : formatPercent(value);
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
/** A move's tone as it is printed: one that rounds to 0.00% is neutral, never red or green. */
|
|
100
|
+
function moveTone(fraction: number): StatItem["tone"] {
|
|
101
|
+
const shown = Number((fraction * 100).toFixed(2));
|
|
102
|
+
return shown > 0 ? "positive" : shown < 0 ? "negative" : "neutral";
|
|
103
|
+
}
|
|
104
|
+
|
|
98
105
|
function sortValue(row: ReturnRow, id: ReturnColumnId): number | null {
|
|
99
106
|
switch (id) {
|
|
100
107
|
case "time":
|
|
@@ -127,7 +134,7 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
127
134
|
symbol && dataProvider?.getChartResolutionSupport
|
|
128
135
|
? dataProvider.getChartResolutionSupport(symbol, exchange)
|
|
129
136
|
: NO_SUPPORT,
|
|
130
|
-
).catch((
|
|
137
|
+
).catch(logFailure("ticker-detail.data-panes.returns-pane", "Read return interval support failed", NO_SUPPORT)), [dataProvider, exchange, symbol]);
|
|
131
138
|
const [support, setSupport] = useState<readonly ChartResolutionSupport[] | null>(null);
|
|
132
139
|
useEffect(() => {
|
|
133
140
|
let cancelled = false;
|
|
@@ -162,15 +169,20 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
162
169
|
() => data ? buildReturnRows(data.points, data.visibleFrom, data.visibleTo) : [],
|
|
163
170
|
[data],
|
|
164
171
|
);
|
|
172
|
+
const barSize = data?.resolution ?? shownResolution;
|
|
173
|
+
const venue = data?.venue || undefined;
|
|
174
|
+
// The times are dated on the venue's own clock, which the column's header names.
|
|
175
|
+
const timeHeader = returnTimeHeader(barSize, venue);
|
|
176
|
+
const columns = useMemo(() => COLUMNS.map((column) => column.id === "time" ? { ...column, label: timeHeader } : column), [timeHeader]);
|
|
165
177
|
const table = useMemo(() => {
|
|
166
178
|
const searchable = returnRows.length >= SEARCH_MIN_ROWS;
|
|
167
179
|
const needle = searchable ? query.trim().toLowerCase() : "";
|
|
168
180
|
const rows = (needle
|
|
169
|
-
? returnRows.filter((row) =>
|
|
181
|
+
? returnRows.filter((row) => returnTimeText(row.timestamp, barSize, venue).includes(needle))
|
|
170
182
|
: returnRows.slice()
|
|
171
183
|
).sort((left, right) => compareSortValues(sortValue(left, sort.columnId), sortValue(right, sort.columnId), sort.direction));
|
|
172
184
|
return { rows, searchable };
|
|
173
|
-
}, [query, returnRows, sort]);
|
|
185
|
+
}, [barSize, query, returnRows, sort, venue]);
|
|
174
186
|
// The quote knows the instrument type even when the saved ticker does not,
|
|
175
187
|
// so prices take the same decimals as Historical Prices.
|
|
176
188
|
const { financials } = usePaneTicker();
|
|
@@ -199,7 +211,6 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
199
211
|
// Today's daily bar is the session still trading, marked as Historical Prices marks it.
|
|
200
212
|
const dayToDate = !!data && isTodayReturnRow(returnRows[0], data.resolution, data.venue || undefined, Date.now());
|
|
201
213
|
const formingKey = forming || dayToDate ? returnRows[0]?.key : undefined;
|
|
202
|
-
const barSize = data?.resolution ?? shownResolution;
|
|
203
214
|
// The close every total is measured from: `from Mon 21 Sep close`.
|
|
204
215
|
const baseText = useMemo(() => {
|
|
205
216
|
const day = data && returnRows.length ? returnBaseCloseDay(data.points, data.visibleFrom, data.resolution, data.venue || undefined, Date.now()) : null;
|
|
@@ -225,6 +236,29 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
225
236
|
title: "Price history data",
|
|
226
237
|
});
|
|
227
238
|
|
|
239
|
+
// The window's total and its best and worst interval, over every bar it holds, whatever the search narrows the table to.
|
|
240
|
+
const summary = useMemo<StatItem[]>(() => {
|
|
241
|
+
const { total, best, worst } = summarizeReturns(returnRows);
|
|
242
|
+
const moveItem = (id: string, label: string, row: ReturnRow | null): StatItem[] => {
|
|
243
|
+
if (row?.intervalPercent == null) return [];
|
|
244
|
+
const when = historyBarDate(returnTimeText(row.timestamp, barSize, venue), barSize, false);
|
|
245
|
+
return [{
|
|
246
|
+
id,
|
|
247
|
+
label,
|
|
248
|
+
value: formatPercent(row.intervalPercent),
|
|
249
|
+
tone: moveTone(row.intervalPercent),
|
|
250
|
+
detail: when,
|
|
251
|
+
// A narrow pane keeps the day without its year, so the three figures stay on one row.
|
|
252
|
+
...isIntradayResolution(barSize) ? { detailVariants: [when.slice(5)] } : {},
|
|
253
|
+
}];
|
|
254
|
+
};
|
|
255
|
+
return [
|
|
256
|
+
...total === null ? [] : [{ id: "total", label: "Total", value: formatPercent(total), tone: moveTone(total) }],
|
|
257
|
+
...moveItem("best", "Best", best),
|
|
258
|
+
...moveItem("worst", "Worst", worst),
|
|
259
|
+
];
|
|
260
|
+
}, [barSize, returnRows, venue]);
|
|
261
|
+
|
|
228
262
|
const chooseSort = useCallback((id: string) => {
|
|
229
263
|
setSort((current) => nextHeaderSort(current, id as ReturnColumnId, { firstDirection: "desc" }));
|
|
230
264
|
}, []);
|
|
@@ -232,10 +266,10 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
232
266
|
const renderCell = useCallback((row: ReturnRow, column: ReturnColumn): DataTableCell => {
|
|
233
267
|
switch (column.id) {
|
|
234
268
|
case "time": {
|
|
235
|
-
const shown =
|
|
269
|
+
const shown = returnTimeText(row.timestamp, barSize, venue);
|
|
236
270
|
// A daily or longer bar is a date; a month's reads as the month, and one still running says so.
|
|
237
271
|
return {
|
|
238
|
-
text:
|
|
272
|
+
text: isIntradayResolution(barSize) ? shown : historyBarDate(shown, barSize, row.key === formingKey),
|
|
239
273
|
value: new Date(row.timestamp).toISOString(),
|
|
240
274
|
color: colors.textDim,
|
|
241
275
|
};
|
|
@@ -243,15 +277,15 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
243
277
|
case "price":
|
|
244
278
|
return { text: formatMaybePrice(row.price, priceDecimals, column.width), value: exportPrice(row.price, priceDecimals), color: colors.textBright, attributes: TextAttributes.BOLD };
|
|
245
279
|
case "intervalChange":
|
|
246
|
-
return { text: formatMaybePrice(row.intervalChange, priceDecimals, column.width), value: exportPrice(row.intervalChange, priceDecimals), color:
|
|
280
|
+
return { text: formatMaybePrice(row.intervalChange, priceDecimals, column.width), value: exportPrice(row.intervalChange, priceDecimals), color: shownPriceColor(row.intervalChange ?? 0, priceDecimals) };
|
|
247
281
|
case "intervalPercent":
|
|
248
|
-
return { text: formatMaybePercent(row.intervalPercent), value: row.intervalPercent === null ? null : row.intervalPercent * 100, color:
|
|
282
|
+
return { text: formatMaybePercent(row.intervalPercent), value: row.intervalPercent === null ? null : row.intervalPercent * 100, color: shownPriceColor((row.intervalPercent ?? 0) * 100, 2) };
|
|
249
283
|
case "cumulativeChange":
|
|
250
|
-
return { text: formatMaybePrice(row.cumulativeChange, priceDecimals, column.width), value: exportPrice(row.cumulativeChange, priceDecimals), color:
|
|
284
|
+
return { text: formatMaybePrice(row.cumulativeChange, priceDecimals, column.width), value: exportPrice(row.cumulativeChange, priceDecimals), color: shownPriceColor(row.cumulativeChange ?? 0, priceDecimals) };
|
|
251
285
|
case "cumulativePercent":
|
|
252
|
-
return { text: formatMaybePercent(row.cumulativePercent), value: row.cumulativePercent === null ? null : row.cumulativePercent * 100, color:
|
|
286
|
+
return { text: formatMaybePercent(row.cumulativePercent), value: row.cumulativePercent === null ? null : row.cumulativePercent * 100, color: shownPriceColor((row.cumulativePercent ?? 0) * 100, 2) };
|
|
253
287
|
}
|
|
254
|
-
}, [barSize, formingKey, priceDecimals]);
|
|
288
|
+
}, [barSize, formingKey, priceDecimals, venue]);
|
|
255
289
|
|
|
256
290
|
return (
|
|
257
291
|
<DataTableView<ReturnRow, ReturnColumn>
|
|
@@ -259,7 +293,7 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
259
293
|
selection={{ kind: "none" }}
|
|
260
294
|
rootWidth={width}
|
|
261
295
|
rootHeight={height}
|
|
262
|
-
columns={
|
|
296
|
+
columns={columns}
|
|
263
297
|
items={table.rows}
|
|
264
298
|
sortColumnId={sort.columnId}
|
|
265
299
|
sortDirection={sort.direction}
|
|
@@ -268,33 +302,36 @@ export function ReturnsPane({ focused, width, height }: PaneProps) {
|
|
|
268
302
|
renderCell={renderCell}
|
|
269
303
|
selectedTextOverridesCellColor
|
|
270
304
|
rootBefore={(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
value
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
<>
|
|
306
|
+
<QueryBar
|
|
307
|
+
width={width}
|
|
308
|
+
search={table.searchable ? {
|
|
309
|
+
value: query,
|
|
310
|
+
onChange: setQuery,
|
|
311
|
+
placeholder: "date or time",
|
|
312
|
+
focused,
|
|
313
|
+
...searchProps,
|
|
314
|
+
} : undefined}
|
|
315
|
+
filters={[{
|
|
316
|
+
id: "range",
|
|
317
|
+
label: "Range",
|
|
318
|
+
value: range,
|
|
319
|
+
options: RETURN_RANGES.map((value) => ({ value, label: value })),
|
|
320
|
+
onChange: setRange,
|
|
321
|
+
}, {
|
|
322
|
+
id: "interval",
|
|
323
|
+
label: "Interval",
|
|
324
|
+
value: shownResolution,
|
|
325
|
+
options: INTERVAL_OPTIONS.map((value) => ({
|
|
326
|
+
value,
|
|
327
|
+
label: getChartResolutionLabel(value),
|
|
328
|
+
disabled: support !== null && !availableResolutions.has(value),
|
|
329
|
+
})),
|
|
330
|
+
onChange: setResolution,
|
|
331
|
+
}]}
|
|
332
|
+
/>
|
|
333
|
+
<StatGrid items={summary} width={width} />
|
|
334
|
+
</>
|
|
298
335
|
)}
|
|
299
336
|
getExportMetadata={() => [
|
|
300
337
|
["Ticker", symbol ? publicTickerKey(symbol, exchange) : ""],
|
|
@@ -4,7 +4,7 @@ import { EXTENDED_SESSION_LABELS, getQuoteSessionFields, quoteHeadlineTime, type
|
|
|
4
4
|
import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneFreshness, HeadlessPaneOptionValues } from "../../../types/headless";
|
|
5
5
|
import type { TimeRange } from "../../../time-series/range";
|
|
6
6
|
import { formatNumber, formatPercentRaw } from "../../../utils/format";
|
|
7
|
-
import { formatMarketPrice, formatMarketPriceWithCurrency, formatPriceObservation, quoteFormatOptions, withCurrencyMinorDigits, type MarketFormatOptions } from "../../../market-data/market/format";
|
|
7
|
+
import { formatMarketPrice, formatMarketPriceWithCurrency, formatPriceObservation, penceFigure, quoteFormatOptions, withCurrencyMinorDigits, type MarketFormatOptions } from "../../../market-data/market/format";
|
|
8
8
|
import {
|
|
9
9
|
MISSING_VOLUME_TEXT, missingVolumeCount, missingVolumeNotice, pricePointValues, priceHistoryIntegrityNotice,
|
|
10
10
|
} from "../../../utils/price-history-integrity";
|
|
@@ -36,7 +36,8 @@ import {
|
|
|
36
36
|
} from "../shared/headless-market-data";
|
|
37
37
|
import { barHistoryFreshness, quoteFreshnessFields, REPORTED_DATA } from "../shared/report-freshness";
|
|
38
38
|
import {
|
|
39
|
-
BAR_INTERVAL_LABELS, barPriceDecimals, cleanFloat32Price, formatBarPrice, historyAllStart, historyAllStartNote, historyMissingPeriodsNote, historyPriceDecimals,
|
|
39
|
+
BAR_INTERVAL_LABELS, barPriceDecimals, cleanBarPrice, cleanFloat32Price, formatBarPrice, historyAllStart, historyAllStartNote, historyMissingPeriodsNote, historyPriceDecimals,
|
|
40
|
+
quotedPriceDivisor,
|
|
40
41
|
} from "../../../cli/history-rows";
|
|
41
42
|
import { formatUtcTime } from "../../../utils/utc-time";
|
|
42
43
|
import { maxDrawdown, maxDrawdownText } from "../../../utils/max-drawdown";
|
|
@@ -197,7 +198,7 @@ async function loadRatioReport(
|
|
|
197
198
|
const history = oldest && tab.key === "valuation"
|
|
198
199
|
? await resolveHeadlessInstrument(ctx, symbol)
|
|
199
200
|
.then(({ symbol: bare, exchange }) => loadPeriodEndHistory(ctx.marketData, bare, exchange ?? "", oldest))
|
|
200
|
-
.catch((
|
|
201
|
+
.catch(logFailure("ticker-detail.headless", "Load valuation price history failed", null))
|
|
201
202
|
: null;
|
|
202
203
|
const table = hasRequestedStatements ? ratioTableForFinancials(financials, tab, period, history) : null;
|
|
203
204
|
const statementCurrency = financialStatementCurrency(financials, [
|
|
@@ -339,12 +340,16 @@ export const quoteComparisonHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
339
340
|
* report declares before it has bars keep every digit the source sent. A bar without
|
|
340
341
|
* volume in a history that reports it (`volumeMissing`) prints it as missing, never as 0.
|
|
341
342
|
*/
|
|
342
|
-
const historyPriceColumns = (volumeMissing: boolean, decimals?: number): HeadlessPaneColumn[] => ["open", "high", "low", "close", "volume"].map((key) => ({
|
|
343
|
+
const historyPriceColumns = (volumeMissing: boolean, decimals?: number, pence?: (close: number) => string | null): HeadlessPaneColumn[] => ["open", "high", "low", "close", "volume"].map((key) => ({
|
|
343
344
|
key, header: key, align: "right" as const,
|
|
344
|
-
format: (value: unknown) =>
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
345
|
+
format: (value: unknown) => {
|
|
346
|
+
if (value == null || !Number.isFinite(Number(value))) return key === "volume" && volumeMissing ? MISSING_VOLUME_TEXT : "-";
|
|
347
|
+
if (key === "volume") return formatNumber(Number(value), 0);
|
|
348
|
+
if (decimals == null) return formatPriceObservation(Number(value), { minimumFractionDigits: 2 });
|
|
349
|
+
// A London line's close also reads in pence, as the pane's close notes it: 6.385 638.5p.
|
|
350
|
+
const note = key === "close" ? pence?.(Number(value)) : null;
|
|
351
|
+
return `${formatBarPrice(Number(value), decimals)}${note ? ` ${note}` : ""}`;
|
|
352
|
+
},
|
|
348
353
|
}));
|
|
349
354
|
|
|
350
355
|
/**
|
|
@@ -353,7 +358,14 @@ const historyPriceColumns = (volumeMissing: boolean, decimals?: number): Headles
|
|
|
353
358
|
* whose period runs on (`2026-10 MTD`); the export keeps each bar's own date
|
|
354
359
|
* and adds a `Partial` column when one is.
|
|
355
360
|
*/
|
|
356
|
-
function historyColumns(
|
|
361
|
+
function historyColumns(
|
|
362
|
+
size: HistoryBarSize,
|
|
363
|
+
partial: boolean,
|
|
364
|
+
text: boolean,
|
|
365
|
+
volumeMissing = false,
|
|
366
|
+
decimals?: number,
|
|
367
|
+
pence?: (close: number) => string | null,
|
|
368
|
+
): HeadlessPaneColumn[] {
|
|
357
369
|
const daily = isDailyOrLongerBar(size);
|
|
358
370
|
const date: HeadlessPaneColumn = {
|
|
359
371
|
key: "date",
|
|
@@ -365,7 +377,7 @@ function historyColumns(size: HistoryBarSize, partial: boolean, text: boolean, v
|
|
|
365
377
|
const marked: HeadlessPaneColumn[] = partial && !text
|
|
366
378
|
? [{ key: "partial", header: "Partial", format: (value) => value === true ? "true" : "" }]
|
|
367
379
|
: [];
|
|
368
|
-
return [date, ...historyPriceColumns(volumeMissing, decimals), ...marked];
|
|
380
|
+
return [date, ...historyPriceColumns(volumeMissing, decimals, text ? pence : undefined), ...marked];
|
|
369
381
|
}
|
|
370
382
|
|
|
371
383
|
export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
@@ -386,14 +398,16 @@ export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
386
398
|
window ? loadPriceWindowHistory(ctx.marketData, listing, exchange ?? "", window).then((result) => result.points)
|
|
387
399
|
: loadHeadlessPriceHistory(ctx, symbol, range),
|
|
388
400
|
pastWindow || (!window && isIntradayResolution(RANGE_HISTORY_RESOLUTION[range])) ? null
|
|
389
|
-
: ctx.marketData.getQuote(listing, exchange ?? "").catch((
|
|
401
|
+
: ctx.marketData.getQuote(listing, exchange ?? "").catch(logFailure("ticker-detail.headless", "Load historical prices quote failed", null)),
|
|
390
402
|
]);
|
|
391
403
|
// The venue whose sessions say whether a day, week or month is still running: the key's, or for a bare symbol its quote's.
|
|
392
404
|
const venue = exchange || quote?.listingExchangeName || quote?.exchangeName || undefined;
|
|
393
405
|
const now = Date.now();
|
|
394
406
|
const points = withSessionBar(loaded, quote, { now, venue });
|
|
395
|
-
// Providers serve float32 prices (4013.39990234 for 4013.4): the rows hold the decimal each is, as `history` does
|
|
396
|
-
|
|
407
|
+
// Providers serve float32 prices (4013.39990234 for 4013.4): the rows hold the decimal each is, as `history` does,
|
|
408
|
+
// a London line's cleaned in the pence it was priced in.
|
|
409
|
+
const priceDivisor = quotedPriceDivisor(quote);
|
|
410
|
+
const clean = (value: number | null) => value == null ? null : cleanBarPrice(value, priceDivisor);
|
|
397
411
|
const rows = points.map((point) => {
|
|
398
412
|
const date = new Date(point.date);
|
|
399
413
|
const values = pricePointValues(point);
|
|
@@ -417,7 +431,7 @@ export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
417
431
|
&& quote.lastUpdated >= Date.parse(`${rows.at(-1)!.date.slice(0, 10)}T00:00:00Z`) ? new Date(quote.lastUpdated).toISOString() : null;
|
|
418
432
|
const shown = forming ? [...rows.slice(0, -1), { ...rows.at(-1)!, partial: true }] : rows;
|
|
419
433
|
// One decimal count for the table, by the instrument's display rules when the quote names its kind.
|
|
420
|
-
const decimals = barPriceDecimals(rows, quoteFormatOptions(quote).assetCategory);
|
|
434
|
+
const decimals = barPriceDecimals(rows, quoteFormatOptions(quote).assetCategory, priceDivisor);
|
|
421
435
|
const marked = shown.some((row) => row.partial === true);
|
|
422
436
|
const volumeMissing = missingVolumeCount(shown);
|
|
423
437
|
const volumeNote = missingVolumeNotice(volumeMissing, shown.length);
|
|
@@ -443,7 +457,8 @@ export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
443
457
|
return {
|
|
444
458
|
rows: shown,
|
|
445
459
|
columns: historyColumns(size, marked, false, false, decimals),
|
|
446
|
-
textColumns: historyColumns(size, marked, true, volumeMissing > 0, decimals
|
|
460
|
+
textColumns: historyColumns(size, marked, true, volumeMissing > 0, decimals,
|
|
461
|
+
(close) => penceFigure(Number(close.toFixed(Math.min(20, decimals))), quote, decimals)),
|
|
447
462
|
freshness,
|
|
448
463
|
...(notes.length > 0 ? { notes } : {}),
|
|
449
464
|
...(notice ? { complete: false } : {}),
|
|
@@ -474,7 +489,7 @@ export const returnsHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
474
489
|
// --from 2007 --to 2009 measures those dates' returns in place of the range.
|
|
475
490
|
const window = readReportWindow(options);
|
|
476
491
|
const pastWindow = !!window && isPastPriceWindow(window);
|
|
477
|
-
const support = await Promise.resolve(ctx.marketData.getChartResolutionSupport?.(symbol, exchange ?? "") ?? []).catch((
|
|
492
|
+
const support = await Promise.resolve(ctx.marketData.getChartResolutionSupport?.(symbol, exchange ?? "") ?? []).catch(logFailure("ticker-detail.headless", "Read return interval support failed", []));
|
|
478
493
|
const now = Date.now();
|
|
479
494
|
const series = await loadReturnSeries(ctx.marketData, {
|
|
480
495
|
symbol, exchange: exchange ?? "", range, resolution: requested, support, now, forceRefresh: ctx.refresh, window,
|
|
@@ -221,7 +221,7 @@ export function buildOverviewStats({
|
|
|
221
221
|
const count = ratioCount(pair.ratio);
|
|
222
222
|
stats.push(receiptSide
|
|
223
223
|
? { label: "ADR Ratio", value: `1:${count}`, detail: [adrRatioText(pair.ratio)!, `${count} sh per ADR`], link: ADR_PAIR }
|
|
224
|
-
: { label: "ADR", value: pair.receipt, detail: [`1 ADR = ${count} of these
|
|
224
|
+
: { label: "ADR", value: pair.receipt, detail: [`1 ADR = ${count} of these shares`, `1:${count}`], link: ADR_PAIR });
|
|
225
225
|
}
|
|
226
226
|
const trailingPE = liveTrailingPE(quote, fundamentals);
|
|
227
227
|
if (trailingPE != null) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
1
2
|
import { Box } from "../../../ui";
|
|
2
3
|
import { getCurrentPluginTarget } from "../../current-target";
|
|
3
4
|
import { recordResearchActivity, recordResearchTabView } from "../../../api-client/research-activity";
|
|
@@ -283,7 +284,7 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
|
|
|
283
284
|
choices={choices.map((item) => ({ ...item, label: t(item.label) }))} />,
|
|
284
285
|
}).then((templateId) => {
|
|
285
286
|
if (templateId && choices.some((item) => item.id === templateId)) createPaneFromTemplate(templateId, { symbol: companySymbol });
|
|
286
|
-
}).catch((
|
|
287
|
+
}).catch(logFailure("ticker-detail.pane", "Open company research choices failed"));
|
|
287
288
|
} }];
|
|
288
289
|
}, [companySymbol, createPaneFromTemplate, dialog, disabledPlugins, registry, tickerResearchTabsSnapshot]);
|
|
289
290
|
usePaneMenuItems("ticker-research:go-to-tab", () => {
|
|
@@ -304,7 +305,7 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
|
|
|
304
305
|
),
|
|
305
306
|
}).then((tabId) => {
|
|
306
307
|
if (tabId) selectResearchTab(tabId);
|
|
307
|
-
}).catch((
|
|
308
|
+
}).catch(logFailure("ticker-detail.pane", "Open research tab choices failed"));
|
|
308
309
|
},
|
|
309
310
|
}];
|
|
310
311
|
}, [dialog, resolvedTabId, selectResearchTab, showTabs, tabItems]);
|
|
@@ -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,
|