@gloom-sh/terminal 0.0.0-stage → 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/LICENSE +25 -0
- package/README.md +73 -2
- package/README.zh-CN.md +116 -0
- package/bin/gloom +13 -0
- package/package.json +144 -4
- package/patches/@opentui%2Fcore@0.3.2.patch +624 -0
- package/patches/electrobun@1.18.1.patch +504 -0
- package/patches/react-reconciler@0.33.0.patch +10 -0
- package/src/account/auth-dialog.tsx +88 -0
- package/src/account/auth-form.tsx +276 -0
- package/src/account/auth-model.ts +167 -0
- package/src/account/billing.ts +57 -0
- package/src/account/checkout-handoff.tsx +225 -0
- package/src/account/device-signin-dialog.tsx +210 -0
- package/src/account/device-signin.ts +235 -0
- package/src/account/email-auth-attempt.ts +173 -0
- package/src/account/session.ts +16 -0
- package/src/account/upgrade-dialog.tsx +286 -0
- package/src/account/upgrade-personalization.ts +57 -0
- package/src/account/upgrade-tickers.ts +124 -0
- package/src/account/verification-panel.tsx +63 -0
- package/src/api-client/app-attention.ts +147 -0
- package/src/api-client/askg.ts +710 -0
- package/src/api-client/attention.ts +26 -0
- package/src/api-client/auth.ts +361 -0
- package/src/api-client/awards.ts +215 -0
- package/src/api-client/bank-figures.ts +73 -0
- package/src/api-client/beneficial-owners.ts +96 -0
- package/src/api-client/catalysts.ts +161 -0
- package/src/api-client/central-bank-rates.ts +51 -0
- package/src/api-client/chat-pane-link.ts +47 -0
- package/src/api-client/chat.ts +370 -0
- package/src/api-client/collections.ts +45 -0
- package/src/api-client/commodity-balances.ts +83 -0
- package/src/api-client/company-kpis.ts +174 -0
- package/src/api-client/cot.ts +78 -0
- package/src/api-client/cpi.ts +52 -0
- package/src/api-client/credit-boards.ts +89 -0
- package/src/api-client/credit-documents.ts +113 -0
- package/src/api-client/crypto-etf-flows.ts +37 -0
- package/src/api-client/crypto-markets.ts +205 -0
- package/src/api-client/data.ts +1314 -0
- package/src/api-client/debt-maturities.ts +110 -0
- package/src/api-client/distress.ts +164 -0
- package/src/api-client/doe.ts +109 -0
- package/src/api-client/earnings.ts +98 -0
- package/src/api-client/equity-screener.ts +245 -0
- package/src/api-client/errors.ts +142 -0
- package/src/api-client/estimate-revisions.ts +113 -0
- package/src/api-client/europe-energy.ts +197 -0
- package/src/api-client/exchange-announcements.ts +38 -0
- package/src/api-client/exposure.ts +202 -0
- package/src/api-client/feedback.ts +34 -0
- package/src/api-client/fund-facts.ts +152 -0
- package/src/api-client/futures-curve.ts +186 -0
- package/src/api-client/geo.ts +373 -0
- package/src/api-client/gpu.ts +93 -0
- package/src/api-client/hiring.ts +411 -0
- package/src/api-client/index.ts +1090 -0
- package/src/api-client/insider-signals.ts +159 -0
- package/src/api-client/ipo.ts +99 -0
- package/src/api-client/letters.ts +41 -0
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +130 -0
- package/src/api-client/market-movers.ts +74 -0
- package/src/api-client/members.ts +44 -0
- package/src/api-client/mna.ts +124 -0
- package/src/api-client/money-markets.ts +57 -0
- package/src/api-client/normalizers.ts +266 -0
- package/src/api-client/notes.ts +75 -0
- package/src/api-client/organization-tickers.ts +44 -0
- package/src/api-client/paths.ts +648 -0
- package/src/api-client/perps.ts +219 -0
- package/src/api-client/plan-access.ts +27 -0
- package/src/api-client/plan-rules.ts +87 -0
- package/src/api-client/power.ts +148 -0
- package/src/api-client/rates.ts +56 -0
- package/src/api-client/request.ts +681 -0
- package/src/api-client/research-activity.ts +590 -0
- package/src/api-client/revenue-breakdown.ts +36 -0
- package/src/api-client/session-cookie.ts +6 -0
- package/src/api-client/short-side-publications.ts +37 -0
- package/src/api-client/short-volume.ts +55 -0
- package/src/api-client/social-mentions.ts +63 -0
- package/src/api-client/socket.ts +1109 -0
- package/src/api-client/supply-chain-graph.ts +40 -0
- package/src/api-client/supply-chain.ts +106 -0
- package/src/api-client/swap-curve.ts +35 -0
- package/src/api-client/tape.ts +56 -0
- package/src/api-client/teams.ts +216 -0
- package/src/api-client/telemetry.ts +226 -0
- package/src/api-client/themes.ts +55 -0
- package/src/api-client/theses.ts +150 -0
- package/src/api-client/types.ts +2409 -0
- package/src/api-client/upgrade-experiment.ts +91 -0
- package/src/api-client/views.ts +98 -0
- package/src/api-client/wall-experiments.ts +102 -0
- package/src/api-client/wall-summary.ts +51 -0
- package/src/api-client/web-experiments.ts +139 -0
- package/src/api-client/yield-curves.ts +72 -0
- package/src/app/app-bootstrap-state.ts +121 -0
- package/src/app/command-bar-intro.ts +38 -0
- package/src/app/dialog-bridge.tsx +33 -0
- package/src/app/global-shortcuts.ts +334 -0
- package/src/app/keybindings/actions.ts +298 -0
- package/src/app/keybindings/capture-request.ts +36 -0
- package/src/app/keybindings/chord.ts +304 -0
- package/src/app/keybindings/config.ts +83 -0
- package/src/app/keybindings/index.ts +51 -0
- package/src/app/keybindings/labels.ts +104 -0
- package/src/app/keybindings/react.tsx +35 -0
- package/src/app/keybindings/resolve.ts +295 -0
- package/src/app/language-observer.tsx +13 -0
- package/src/app/pane-runtime/index.tsx +320 -0
- package/src/app/pane-runtime/layout-placement.ts +56 -0
- package/src/app/pane-runtime/pane-settings-runtime.tsx +69 -0
- package/src/app/pane-runtime/pane-template-runtime.ts +123 -0
- package/src/app/pane-runtime/plugin-bindings.ts +541 -0
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +190 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +238 -0
- package/src/app/pane-runtime/ticker-open-target.ts +31 -0
- package/src/app/runtime/broker-import.ts +187 -0
- package/src/app/runtime/desktop-deeplink.ts +641 -0
- package/src/app/runtime/desktop-menu.ts +95 -0
- package/src/app/runtime/layout-warmup.ts +124 -0
- package/src/app/runtime/plugin-bindings.ts +301 -0
- package/src/app/runtime/startup.ts +256 -0
- package/src/app/runtime/ticker-refresh.ts +206 -0
- package/src/app/runtime/update.ts +260 -0
- package/src/app/star-prompt/model.ts +98 -0
- package/src/app/star-prompt/runtime.ts +41 -0
- package/src/app/star-prompt/status-line.tsx +190 -0
- package/src/app.tsx +663 -0
- package/src/architecture/core-builtin-allowlist.json +39 -0
- package/src/architecture/dom-cell-metrics-allowlist.json +102 -0
- package/src/architecture/pane-tabs-allowlist.json +68 -0
- package/src/architecture/silent-catch-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/architecture/ui-literals-allowlist.json +186 -0
- package/src/architecture/ui-tokens-allowlist.json +40 -0
- package/src/ask-gloom/pane-context.ts +130 -0
- package/src/ask-gloom/pane-request.ts +70 -0
- package/src/ask-gloom/pane-text.ts +201 -0
- package/src/assets/gloomberb-logo-windows.ico +0 -0
- package/src/assets/gloomberb-logo-windows.png +0 -0
- package/src/assets/gloomberb-logo.svg +1 -0
- package/src/assets/gloomberb-mark.ico +0 -0
- package/src/assets/gloomberb-mark.svg +1 -0
- package/src/brokers/account-cache.ts +218 -0
- package/src/brokers/broker-portfolio-sync.ts +357 -0
- package/src/brokers/broker-ticker-sync.ts +146 -0
- package/src/brokers/cloud-broker-link.ts +26 -0
- package/src/brokers/directory.ts +76 -0
- package/src/brokers/logos.ts +68 -0
- package/src/brokers/profile-form.ts +113 -0
- package/src/brokers/remote-broker-adapter.ts +115 -0
- package/src/brokers/remove-profile.ts +61 -0
- package/src/brokers/signed-in/adapter.ts +568 -0
- package/src/brokers/signed-in/catalog.ts +130 -0
- package/src/brokers/signed-in/client.ts +81 -0
- package/src/brokers/signed-in/connect.ts +90 -0
- package/src/brokers/signed-in/profile.ts +18 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +339 -0
- package/src/brokers/signed-in/sign-in.ts +237 -0
- package/src/brokers/sync-broker-instance.ts +473 -0
- package/src/capabilities/chart-series.ts +354 -0
- package/src/capabilities/factories.ts +168 -0
- package/src/capabilities/index.ts +42 -0
- package/src/capabilities/registry.ts +181 -0
- package/src/capabilities/service-capabilities.ts +78 -0
- package/src/capabilities/stream-client.ts +39 -0
- package/src/capabilities/types.ts +161 -0
- package/src/cli/bank-section.ts +147 -0
- package/src/cli/base-converter.ts +86 -0
- package/src/cli/board-coins.ts +70 -0
- package/src/cli/central-bank-events.ts +23 -0
- package/src/cli/command-options.ts +141 -0
- package/src/cli/commands/api.ts +142 -0
- package/src/cli/commands/broker.ts +49 -0
- package/src/cli/commands/command-utils.ts +83 -0
- package/src/cli/commands/crypto-hints.ts +119 -0
- package/src/cli/commands/fred-series.ts +319 -0
- package/src/cli/commands/keybindings.ts +125 -0
- package/src/cli/commands/market.ts +3569 -0
- package/src/cli/commands/news-hints.ts +62 -0
- package/src/cli/commands/news-search.ts +166 -0
- package/src/cli/commands/options-chain.ts +355 -0
- package/src/cli/commands/options-leaps.ts +845 -0
- package/src/cli/commands/overview.ts +965 -0
- package/src/cli/commands/plugins.ts +382 -0
- package/src/cli/commands/remote.ts +219 -0
- package/src/cli/commands/search.ts +653 -0
- package/src/cli/commands/system.ts +699 -0
- package/src/cli/commands/themes.ts +73 -0
- package/src/cli/commands/ticker.ts +1672 -0
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/context.ts +159 -0
- package/src/cli/currency-hints.ts +27 -0
- package/src/cli/desktop-pane-shot-routes.ts +7 -0
- package/src/cli/desktop-pane-shot-view.ts +114 -0
- package/src/cli/desktop-pane-shot.ts +1010 -0
- package/src/cli/earnings-calendar.ts +72 -0
- package/src/cli/entry.ts +97 -0
- package/src/cli/errors.ts +170 -0
- package/src/cli/exit-codes.ts +8 -0
- package/src/cli/export-source.ts +150 -0
- package/src/cli/freshness.ts +84 -0
- package/src/cli/fx-forward.ts +130 -0
- package/src/cli/fx-pair.ts +189 -0
- package/src/cli/glossary.ts +1009 -0
- package/src/cli/help.ts +246 -0
- package/src/cli/helpers.ts +169 -0
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +847 -0
- package/src/cli/index-roots.ts +44 -0
- package/src/cli/index.ts +515 -0
- package/src/cli/issuer-membership.ts +103 -0
- package/src/cli/known-gaps.ts +402 -0
- package/src/cli/listing-arg.ts +1529 -0
- package/src/cli/listing-suggestion.ts +174 -0
- package/src/cli/name-entries.ts +127 -0
- package/src/cli/native-smoke.ts +43 -0
- package/src/cli/not-a-ticker.ts +83 -0
- package/src/cli/opentui-libc.ts +5 -0
- package/src/cli/options.ts +166 -0
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/access-gate.ts +75 -0
- package/src/cli/pane-functions/capabilities.ts +367 -0
- package/src/cli/pane-functions/catalog.ts +834 -0
- package/src/cli/pane-functions/cloud-session.ts +47 -0
- package/src/cli/pane-functions/data.ts +254 -0
- package/src/cli/pane-functions/discovery.ts +234 -0
- package/src/cli/pane-functions/dom.ts +425 -0
- package/src/cli/pane-functions/financial-ratio-shot.ts +160 -0
- package/src/cli/pane-functions/freshness.ts +1288 -0
- package/src/cli/pane-functions/function-help.ts +1450 -0
- package/src/cli/pane-functions/headless.ts +1059 -0
- package/src/cli/pane-functions/index.ts +636 -0
- package/src/cli/pane-functions/lock-note.ts +59 -0
- package/src/cli/pane-functions/market-session.ts +297 -0
- package/src/cli/pane-functions/options.ts +410 -0
- package/src/cli/pane-functions/report-notices.ts +121 -0
- package/src/cli/pane-functions/report.ts +102 -0
- package/src/cli/pane-functions/resolver.ts +428 -0
- package/src/cli/pane-functions/screenshot-assessment.ts +324 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
- package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
- package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +131 -0
- package/src/cli/pane-functions/screenshot-payload.ts +453 -0
- package/src/cli/pane-functions/screenshot-render.ts +316 -0
- package/src/cli/pane-functions/screenshot-types.ts +167 -0
- package/src/cli/pane-functions/shot-options.ts +73 -0
- package/src/cli/perpetual-contract.ts +62 -0
- package/src/cli/peso-dollar-rates.ts +79 -0
- package/src/cli/quote-answer.ts +231 -0
- package/src/cli/registry.ts +178 -0
- package/src/cli/report-tables.ts +692 -0
- package/src/cli/restore-plugins.ts +77 -0
- package/src/cli/result.ts +670 -0
- package/src/cli/row-window.ts +76 -0
- package/src/cli/scoped-context.ts +41 -0
- package/src/cli/scroll-truncation.ts +38 -0
- package/src/cli/series-search.ts +117 -0
- package/src/cli/staged-tickers.ts +57 -0
- package/src/cli/statement-filings.ts +280 -0
- package/src/cli/suffixed-listing-rows.ts +49 -0
- package/src/cli/symbol-args.ts +158 -0
- package/src/cli/symbol-cells.ts +120 -0
- package/src/cli/symbol-list.ts +374 -0
- package/src/cli/symbol-rows.ts +517 -0
- package/src/cli/ticker-resolution.ts +566 -0
- package/src/cli/treasury-yields.ts +86 -0
- package/src/cli/types.ts +42 -0
- package/src/cli/uncovered-instrument.ts +20 -0
- package/src/cli/unknown-command.ts +88 -0
- package/src/cli/visible-key-values.ts +29 -0
- package/src/components/chart/composite/axis-overlays.tsx +452 -0
- package/src/components/chart/composite/bitmap.ts +71 -0
- package/src/components/chart/composite/chart-layout.ts +131 -0
- package/src/components/chart/composite/column-layout.ts +215 -0
- package/src/components/chart/composite/composite-chart.tsx +1248 -0
- package/src/components/chart/composite/downsample.ts +221 -0
- package/src/components/chart/composite/drawing-store.ts +79 -0
- package/src/components/chart/composite/format.ts +574 -0
- package/src/components/chart/composite/index.ts +16 -0
- package/src/components/chart/composite/interactions.ts +385 -0
- package/src/components/chart/composite/legend.tsx +623 -0
- package/src/components/chart/composite/level-labels.ts +30 -0
- package/src/components/chart/composite/levels.ts +172 -0
- package/src/components/chart/composite/panel-bitmap.ts +180 -0
- package/src/components/chart/composite/panel-gestures.ts +133 -0
- package/src/components/chart/composite/panel-labels.ts +147 -0
- package/src/components/chart/composite/panel-series.ts +106 -0
- package/src/components/chart/composite/panel-surface.tsx +1328 -0
- package/src/components/chart/composite/price-axis-labels.tsx +320 -0
- package/src/components/chart/composite/price-series.ts +83 -0
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +645 -0
- package/src/components/chart/composite/scene.ts +971 -0
- package/src/components/chart/composite/series-axis-tags.tsx +74 -0
- package/src/components/chart/composite/series-cues.ts +85 -0
- package/src/components/chart/composite/session-shading.ts +100 -0
- package/src/components/chart/composite/text-renderer.ts +407 -0
- package/src/components/chart/composite/time-axis.ts +705 -0
- package/src/components/chart/composite/time-scale.ts +335 -0
- package/src/components/chart/composite/tool-catalog.ts +134 -0
- package/src/components/chart/composite/toolbar.tsx +238 -0
- package/src/components/chart/composite/tools.ts +769 -0
- package/src/components/chart/composite/types.ts +354 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +844 -0
- package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
- package/src/components/chart/composite/use-composite-viewport.ts +180 -0
- package/src/components/chart/composite/value-scale.ts +149 -0
- package/src/components/chart/composite/volume-profile-paint.ts +80 -0
- package/src/components/chart/core/data.ts +9 -0
- package/src/components/chart/core/date-window.ts +179 -0
- package/src/components/chart/core/palette.ts +49 -0
- package/src/components/chart/core/pointer.ts +182 -0
- package/src/components/chart/core/types.ts +22 -0
- package/src/components/chart/core/view-reset.ts +46 -0
- package/src/components/chart/curve/curve-surface.tsx +280 -0
- package/src/components/chart/curve/index.ts +4 -0
- package/src/components/chart/curve/model.ts +354 -0
- package/src/components/chart/native/bitmap-support.ts +55 -0
- package/src/components/chart/native/chart-rasterizer.ts +16 -0
- package/src/components/chart/native/kitty/adapter.ts +9 -0
- package/src/components/chart/native/kitty/manager.ts +145 -0
- package/src/components/chart/native/kitty/protocol.ts +112 -0
- package/src/components/chart/native/kitty/support.ts +104 -0
- package/src/components/chart/native/raster/crosshair.ts +89 -0
- package/src/components/chart/native/raster/placement.ts +138 -0
- package/src/components/chart/native/raster/primitives.ts +209 -0
- package/src/components/chart/native/raster/types.ts +25 -0
- package/src/components/chart/native/renderer-selection.ts +154 -0
- package/src/components/chart/native/surface/manager.ts +290 -0
- package/src/components/chart/native/surface/visibility.ts +85 -0
- package/src/components/chart/native/use-chart-text-fallback.ts +19 -0
- package/src/components/chart/static/chart-surface.tsx +162 -0
- package/src/components/chart/static/index.ts +5 -0
- package/src/components/chart/static/scatter-chart-surface.tsx +184 -0
- package/src/components/chart/static/series.ts +91 -0
- package/src/components/chart/static/trail-chart-model.ts +105 -0
- package/src/components/chart/static/trail-chart-surface.tsx +422 -0
- package/src/components/chart/surface3d/chart.tsx +104 -0
- package/src/components/chart/surface3d/model.ts +426 -0
- package/src/components/chart/surface3d/software.ts +360 -0
- package/src/components/chart-table/axis.ts +49 -0
- package/src/components/chart-table/header.tsx +264 -0
- package/src/components/chart-table/index.ts +13 -0
- package/src/components/chart-table/layout.ts +154 -0
- package/src/components/chart-table/read.tsx +74 -0
- package/src/components/chart-table/selection.ts +145 -0
- package/src/components/command-bar/action-runtime.ts +268 -0
- package/src/components/command-bar/assist/inventory.ts +146 -0
- package/src/components/command-bar/assist/model.ts +279 -0
- package/src/components/command-bar/assist/runtime.ts +246 -0
- package/src/components/command-bar/commands/collection-list.ts +262 -0
- package/src/components/command-bar/commands/collection.ts +285 -0
- package/src/components/command-bar/commands/direct/index.ts +460 -0
- package/src/components/command-bar/commands/direct/runtime.ts +181 -0
- package/src/components/command-bar/commands/plugin/actions.ts +143 -0
- package/src/components/command-bar/commands/plugin/items.ts +178 -0
- package/src/components/command-bar/commands/registry.ts +362 -0
- package/src/components/command-bar/helpers.ts +86 -0
- package/src/components/command-bar/keyboard-handlers.ts +129 -0
- package/src/components/command-bar/keyboard-shortcuts.ts +165 -0
- package/src/components/command-bar/layout-items/current-layout.ts +210 -0
- package/src/components/command-bar/layout-items/focused-pane.ts +193 -0
- package/src/components/command-bar/layout-items/types.ts +26 -0
- package/src/components/command-bar/layout-items/window-mode.ts +72 -0
- package/src/components/command-bar/layout-items.ts +93 -0
- package/src/components/command-bar/list/badge.ts +63 -0
- package/src/components/command-bar/list/model.ts +213 -0
- package/src/components/command-bar/list/navigation.ts +121 -0
- package/src/components/command-bar/list/view.tsx +362 -0
- package/src/components/command-bar/pane-actions.ts +128 -0
- package/src/components/command-bar/pane-settings.ts +44 -0
- package/src/components/command-bar/pane-templates/items.ts +289 -0
- package/src/components/command-bar/pane-templates/workflow-route.ts +91 -0
- package/src/components/command-bar/pane-templates/workflow.ts +344 -0
- package/src/components/command-bar/panel/index.tsx +260 -0
- package/src/components/command-bar/panel/layout.ts +150 -0
- package/src/components/command-bar/panel/native-surface.ts +26 -0
- package/src/components/command-bar/panel/palette.ts +28 -0
- package/src/components/command-bar/panel/prompt-binding.ts +54 -0
- package/src/components/command-bar/panel/prompt-example.ts +22 -0
- package/src/components/command-bar/panel/runtime.ts +230 -0
- package/src/components/command-bar/panel/state.ts +161 -0
- package/src/components/command-bar/panel/types.ts +50 -0
- package/src/components/command-bar/picker-activation.ts +87 -0
- package/src/components/command-bar/routes/root/command-items.ts +253 -0
- package/src/components/command-bar/routes/root/help-items.ts +66 -0
- package/src/components/command-bar/routes/root/pick-prefetch.ts +38 -0
- package/src/components/command-bar/routes/root/plugin-install.ts +304 -0
- package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
- package/src/components/command-bar/routes/root/provider-search.ts +271 -0
- package/src/components/command-bar/routes/root/results.ts +439 -0
- package/src/components/command-bar/routes/root/runtime.ts +396 -0
- package/src/components/command-bar/routes/root/search-providers.ts +311 -0
- package/src/components/command-bar/routes/root/search-report.ts +247 -0
- package/src/components/command-bar/routes/root/selection.ts +250 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +116 -0
- package/src/components/command-bar/routes/root/shortcut-items.ts +161 -0
- package/src/components/command-bar/routes/root/shortcuts.ts +221 -0
- package/src/components/command-bar/routes/root/start-here.ts +90 -0
- package/src/components/command-bar/routes/ticker-search/actions.ts +248 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +328 -0
- package/src/components/command-bar/routes/ticker-search/route.ts +151 -0
- package/src/components/command-bar/routing/actions.ts +194 -0
- package/src/components/command-bar/routing/confirm.ts +23 -0
- package/src/components/command-bar/routing/effects.ts +63 -0
- package/src/components/command-bar/routing/launch-request.ts +41 -0
- package/src/components/command-bar/routing/list-state.ts +196 -0
- package/src/components/command-bar/routing/navigation-state.ts +197 -0
- package/src/components/command-bar/selection-runtime.ts +411 -0
- package/src/components/command-bar/surface/environment.ts +136 -0
- package/src/components/command-bar/surface/index.tsx +633 -0
- package/src/components/command-bar/theme-picker.tsx +336 -0
- package/src/components/command-bar/theme-preview.ts +84 -0
- package/src/components/command-bar/view-model.ts +237 -0
- package/src/components/command-bar/workflow/alert.ts +170 -0
- package/src/components/command-bar/workflow/broker.ts +186 -0
- package/src/components/command-bar/workflow/builtin.ts +174 -0
- package/src/components/command-bar/workflow/collection-actions.ts +398 -0
- package/src/components/command-bar/workflow/collection-ticker.ts +93 -0
- package/src/components/command-bar/workflow/coordinator.ts +72 -0
- package/src/components/command-bar/workflow/fields.ts +252 -0
- package/src/components/command-bar/workflow/ops.ts +459 -0
- package/src/components/command-bar/workflow/pane-reuse.ts +178 -0
- package/src/components/command-bar/workflow/portfolio-position.ts +211 -0
- package/src/components/command-bar/workflow/route-builder.ts +56 -0
- package/src/components/command-bar/workflow/submit.ts +220 -0
- package/src/components/command-bar/workflow/ticker-listing-picker.ts +38 -0
- package/src/components/command-bar/workflow/tickers.ts +288 -0
- package/src/components/command-bar/workflow/types.ts +106 -0
- package/src/components/company-logo.tsx +112 -0
- package/src/components/data-table/export.ts +76 -0
- package/src/components/data-table/sections.ts +57 -0
- package/src/components/data-table/sort-menu.tsx +75 -0
- package/src/components/data-table/stack-view.tsx +114 -0
- package/src/components/data-table/table-pane.ts +46 -0
- package/src/components/data-table/view.tsx +767 -0
- package/src/components/feed-data-table/stack-view.tsx +304 -0
- package/src/components/feed-stack-controller.ts +39 -0
- package/src/components/feedback-dialog.tsx +341 -0
- package/src/components/form-modal/broker-step.tsx +63 -0
- package/src/components/form-modal/confirm.tsx +147 -0
- package/src/components/form-modal/content.tsx +703 -0
- package/src/components/form-modal/deps.ts +77 -0
- package/src/components/form-modal/field-row.tsx +316 -0
- package/src/components/form-modal/host.tsx +169 -0
- package/src/components/form-modal/index.ts +2 -0
- package/src/components/form-modal/model.ts +134 -0
- package/src/components/form-modal/request.ts +74 -0
- package/src/components/form-modal/routes.ts +157 -0
- package/src/components/index.ts +162 -0
- package/src/components/input-search-bar.tsx +171 -0
- package/src/components/layout/detached-pane-shell.tsx +487 -0
- package/src/components/layout/floating-pane.tsx +196 -0
- package/src/components/layout/header.tsx +967 -0
- package/src/components/layout/market-summary.ts +154 -0
- package/src/components/layout/missing-pane.tsx +172 -0
- package/src/components/layout/pane/capture-stamp.ts +41 -0
- package/src/components/layout/pane/capture-status.ts +12 -0
- package/src/components/layout/pane/content.tsx +113 -0
- package/src/components/layout/pane/cycle-order.ts +23 -0
- package/src/components/layout/pane/error-boundary.tsx +103 -0
- package/src/components/layout/pane/footer/index.tsx +406 -0
- package/src/components/layout/pane/footer/keyboard.ts +99 -0
- package/src/components/layout/pane/footer/model.ts +456 -0
- package/src/components/layout/pane/footer/registration.tsx +278 -0
- package/src/components/layout/pane/frame.tsx +56 -0
- package/src/components/layout/pane/header-tabs.tsx +135 -0
- package/src/components/layout/pane/header.tsx +471 -0
- package/src/components/layout/pane/index.tsx +148 -0
- package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
- package/src/components/layout/pane/pane-tabs.tsx +50 -0
- package/src/components/layout/pane/sidebar-metrics.ts +65 -0
- package/src/components/layout/pane/sidebar.tsx +448 -0
- package/src/components/layout/pane/sizing.ts +100 -0
- package/src/components/layout/pane/status-footer.ts +92 -0
- package/src/components/layout/pane/surface.ts +26 -0
- package/src/components/layout/pane/ticker-fate-gate.tsx +62 -0
- package/src/components/layout/pane/title.ts +111 -0
- package/src/components/layout/presentation.ts +22 -0
- package/src/components/layout/shell/action-menu-overlay.tsx +149 -0
- package/src/components/layout/shell/active-drag.ts +266 -0
- package/src/components/layout/shell/chrome.ts +127 -0
- package/src/components/layout/shell/cursor-occlusion.ts +206 -0
- package/src/components/layout/shell/drag/index.ts +456 -0
- package/src/components/layout/shell/drag/live.ts +203 -0
- package/src/components/layout/shell/drag/overlays.tsx +222 -0
- package/src/components/layout/shell/drag/runtime.ts +347 -0
- package/src/components/layout/shell/drag/slide.ts +10 -0
- package/src/components/layout/shell/fullscreen.ts +10 -0
- package/src/components/layout/shell/index.tsx +970 -0
- package/src/components/layout/shell/layout-state.ts +86 -0
- package/src/components/layout/shell/menu.ts +303 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +209 -0
- package/src/components/layout/shell/native/surfaces.ts +143 -0
- package/src/components/layout/shell/native/window-state.ts +208 -0
- package/src/components/layout/shell/pane/actions.ts +223 -0
- package/src/components/layout/shell/pane/ask.ts +136 -0
- package/src/components/layout/shell/pane/capture.ts +177 -0
- package/src/components/layout/shell/pane/layers.tsx +777 -0
- package/src/components/layout/shell/pane/management-shortcuts.ts +239 -0
- package/src/components/layout/shell/pane/new-layout.ts +116 -0
- package/src/components/layout/shell/pane-occlusion.ts +63 -0
- package/src/components/layout/shell/shortcuts.ts +124 -0
- package/src/components/layout/shell/terminal-pointer-runtime.ts +380 -0
- package/src/components/layout/shell/ticker-link-menu.ts +115 -0
- package/src/components/layout/shell/visible-layout.ts +60 -0
- package/src/components/layout/shell/window-mode/index.ts +271 -0
- package/src/components/layout/shell/window-mode/overlays.tsx +225 -0
- package/src/components/layout/status-bar-groups.ts +85 -0
- package/src/components/layout/status-bar.tsx +755 -0
- package/src/components/layout/status-widget-space.ts +72 -0
- package/src/components/layout/titlebar-overlay.ts +17 -0
- package/src/components/layout/transient-layout.tsx +60 -0
- package/src/components/layout/window-controls.tsx +104 -0
- package/src/components/layout/window-edit/mode.ts +509 -0
- package/src/components/layout/window-edit/presentation.ts +138 -0
- package/src/components/layout/window-edit/status.tsx +121 -0
- package/src/components/layout/window-fullscreen.ts +32 -0
- package/src/components/markdown-editor.tsx +150 -0
- package/src/components/markdown-text.tsx +645 -0
- package/src/components/market-board/index.tsx +224 -0
- package/src/components/metric-treemap/heat-surface.tsx +828 -0
- package/src/components/metric-treemap/heat-tiles.ts +261 -0
- package/src/components/metric-treemap/index.tsx +352 -0
- package/src/components/metric-treemap/layout.ts +484 -0
- package/src/components/metric-treemap/scene.ts +432 -0
- package/src/components/onboarding/account-step/form-panel.tsx +196 -0
- package/src/components/onboarding/account-step/index.tsx +51 -0
- package/src/components/onboarding/account-step/qr-panel.tsx +47 -0
- package/src/components/onboarding/account-step/signed-in-panel.tsx +28 -0
- package/src/components/onboarding/desks-step.tsx +43 -0
- package/src/components/onboarding/first-run-workspace.ts +223 -0
- package/src/components/onboarding/onboarding-frame.tsx +597 -0
- package/src/components/onboarding/onboarding-keyboard.ts +311 -0
- package/src/components/onboarding/onboarding-steps.tsx +2 -0
- package/src/components/onboarding/onboarding-wizard.tsx +745 -0
- package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +177 -0
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +121 -0
- package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +40 -0
- package/src/components/onboarding/portfolio-step/choose-panel.tsx +39 -0
- package/src/components/onboarding/portfolio-step/index.tsx +74 -0
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +379 -0
- package/src/components/onboarding/portfolio-step/types.ts +34 -0
- package/src/components/onboarding/portfolio-step/utils.ts +15 -0
- package/src/components/onboarding/pro-step-layout.ts +86 -0
- package/src/components/onboarding/use-onboarding-broker-form.ts +266 -0
- package/src/components/onboarding/use-onboarding-progress.ts +298 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +87 -0
- package/src/components/onboarding/wizard-account.ts +182 -0
- package/src/components/onboarding/wizard-broker-sync.ts +281 -0
- package/src/components/onboarding/wizard-model.ts +161 -0
- package/src/components/onboarding/wizard-positions.ts +434 -0
- package/src/components/paged-rows.ts +217 -0
- package/src/components/pane-settings-dialog/desktop.tsx +261 -0
- package/src/components/pane-settings-dialog/field-dialogs.tsx +214 -0
- package/src/components/pane-settings-dialog/tui.tsx +121 -0
- package/src/components/pane-settings-dialog/value.ts +68 -0
- package/src/components/pane-settings-dialog.tsx +248 -0
- package/src/components/pane-template-wizard.tsx +60 -0
- package/src/components/price-performance.tsx +85 -0
- package/src/components/price-selector-dialog.tsx +215 -0
- package/src/components/price-sparkline/bitmap.ts +150 -0
- package/src/components/price-sparkline/index.ts +73 -0
- package/src/components/price-sparkline/model.ts +150 -0
- package/src/components/price-sparkline/view.tsx +226 -0
- package/src/components/quote-flash.ts +168 -0
- package/src/components/reader/article-reader.tsx +468 -0
- package/src/components/reader/article-text.ts +527 -0
- package/src/components/reader/detail-reader.tsx +239 -0
- package/src/components/reader/reader-text.tsx +281 -0
- package/src/components/sign-in-code-panel.tsx +203 -0
- package/src/components/sign-in-gate.tsx +169 -0
- package/src/components/speedometer-gauge/desktop.tsx +142 -0
- package/src/components/speedometer-gauge/index.tsx +42 -0
- package/src/components/speedometer-gauge/model.ts +57 -0
- package/src/components/speedometer-gauge/terminal.tsx +283 -0
- package/src/components/table-view-shared.tsx +243 -0
- package/src/components/textarea-keys.ts +13 -0
- package/src/components/ticker/badge/format.ts +121 -0
- package/src/components/ticker/badge/index.tsx +144 -0
- package/src/components/ticker/badge/list.tsx +146 -0
- package/src/components/ticker/badge/text.tsx +157 -0
- package/src/components/ticker/list-table-view.tsx +421 -0
- package/src/components/toggle-list.tsx +194 -0
- package/src/components/ui/action-row.tsx +36 -0
- package/src/components/ui/action-scope.tsx +96 -0
- package/src/components/ui/audio-player.tsx +70 -0
- package/src/components/ui/button.tsx +189 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/checkbox.tsx +80 -0
- package/src/components/ui/choice-dialog.tsx +125 -0
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/column-grid.tsx +250 -0
- package/src/components/ui/confirm-dialog.tsx +175 -0
- package/src/components/ui/country-flag-art.tsx +629 -0
- package/src/components/ui/country-flag.tsx +147 -0
- package/src/components/ui/data-table/hooks.ts +46 -0
- package/src/components/ui/data-table/index.tsx +179 -0
- package/src/components/ui/data-table/opentui/index.tsx +796 -0
- package/src/components/ui/data-table/opentui/model.ts +281 -0
- package/src/components/ui/data-table/stable-columns.ts +34 -0
- package/src/components/ui/data-table/types.ts +191 -0
- package/src/components/ui/data-table/visible-range.ts +47 -0
- package/src/components/ui/detail-scroll-body.tsx +75 -0
- package/src/components/ui/dialog-actions.tsx +44 -0
- package/src/components/ui/disclosure-marker.tsx +38 -0
- package/src/components/ui/display.tsx +260 -0
- package/src/components/ui/external-link.tsx +181 -0
- package/src/components/ui/field-grid.tsx +473 -0
- package/src/components/ui/field-ring.ts +125 -0
- package/src/components/ui/fields.tsx +425 -0
- package/src/components/ui/figure.tsx +96 -0
- package/src/components/ui/fill.ts +8 -0
- package/src/components/ui/frame.tsx +149 -0
- package/src/components/ui/header-case.ts +30 -0
- package/src/components/ui/icon.tsx +199 -0
- package/src/components/ui/index.ts +84 -0
- package/src/components/ui/inline-quick-add.tsx +144 -0
- package/src/components/ui/list-row-lines.tsx +87 -0
- package/src/components/ui/list-view.tsx +403 -0
- package/src/components/ui/loading.tsx +17 -0
- package/src/components/ui/logo-mark.tsx +73 -0
- package/src/components/ui/menu.tsx +152 -0
- package/src/components/ui/message-composer.tsx +172 -0
- package/src/components/ui/multi-select/dialog.tsx +643 -0
- package/src/components/ui/multi-select/index.ts +190 -0
- package/src/components/ui/number-prompt-dialog.tsx +71 -0
- package/src/components/ui/number-step.ts +54 -0
- package/src/components/ui/page-stack-view.tsx +137 -0
- package/src/components/ui/popover.tsx +10 -0
- package/src/components/ui/prose.tsx +280 -0
- package/src/components/ui/query-bar-keys.tsx +196 -0
- package/src/components/ui/query-bar.tsx +554 -0
- package/src/components/ui/quote.ts +42 -0
- package/src/components/ui/range-track.tsx +66 -0
- package/src/components/ui/ratio-bar.tsx +84 -0
- package/src/components/ui/remote-image.tsx +37 -0
- package/src/components/ui/reveal-in-scroll-box.ts +52 -0
- package/src/components/ui/row-notes.ts +79 -0
- package/src/components/ui/row-value-cache.ts +41 -0
- package/src/components/ui/select-button.tsx +219 -0
- package/src/components/ui/select-field.tsx +55 -0
- package/src/components/ui/shortcut-hint.tsx +69 -0
- package/src/components/ui/split-bar.tsx +69 -0
- package/src/components/ui/stat-grid.tsx +198 -0
- package/src/components/ui/status.tsx +347 -0
- package/src/components/ui/table-layout.ts +259 -0
- package/src/components/ui/tabs.tsx +587 -0
- package/src/components/ui/text-prompt-dialog.tsx +135 -0
- package/src/components/ui/toggle.tsx +136 -0
- package/src/components/ui/windowed-list.tsx +27 -0
- package/src/components/use-double-click-activation.ts +40 -0
- package/src/components/use-external-link-footer.ts +82 -0
- package/src/components/use-pane-notice-footer.tsx +128 -0
- package/src/components/use-pinned-moment.ts +19 -0
- package/src/components/use-scrollbox-layout.ts +17 -0
- package/src/core/app-runtime.ts +136 -0
- package/src/core/app-service-ports.ts +54 -0
- package/src/core/app-services.ts +48 -0
- package/src/core/connection-health.ts +293 -0
- package/src/core/state/app/instrument.ts +99 -0
- package/src/core/state/app/layout-reducer.ts +405 -0
- package/src/core/state/app/layout.ts +613 -0
- package/src/core/state/app/state.ts +401 -0
- package/src/core/state/app/types.ts +201 -0
- package/src/core/state/session-persistence.ts +150 -0
- package/src/data/app-persistence.ts +25 -0
- package/src/data/cached-query.ts +120 -0
- package/src/data/config/chart-settings.ts +709 -0
- package/src/data/config/home.ts +123 -0
- package/src/data/config/layout.ts +317 -0
- package/src/data/config/store/fold-panes.ts +340 -0
- package/src/data/config/store/index.ts +63 -0
- package/src/data/config/store/migrations.ts +294 -0
- package/src/data/config/store/node.ts +178 -0
- package/src/data/config/store/normalize.ts +517 -0
- package/src/data/config/store/pane-state.ts +47 -0
- package/src/data/config/store/portfolio-as-of-column.ts +57 -0
- package/src/data/json-persistence.ts +85 -0
- package/src/data/json-storage.ts +57 -0
- package/src/data/json-ticker-repository.ts +67 -0
- package/src/data/memory-resource-store.ts +152 -0
- package/src/data/plugin-cache.ts +119 -0
- package/src/data/plugin-state-store.ts +150 -0
- package/src/data/resource-store.ts +434 -0
- package/src/data/response-sequence.ts +6 -0
- package/src/data/saved-copies.ts +123 -0
- package/src/data/session-store.ts +59 -0
- package/src/data/sqlite/database.ts +80 -0
- package/src/data/sqlite/json.ts +18 -0
- package/src/data/sqlite/retry.ts +90 -0
- package/src/data/ticker-repository.ts +60 -0
- package/src/data/ticker-store.ts +65 -0
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/content.ts +215 -0
- package/src/docs/load.ts +139 -0
- package/src/docs/requests.ts +39 -0
- package/src/docs/snapshot.chunk.ts +8 -0
- package/src/docs/snapshot.json +8106 -0
- package/src/docs/topics.ts +216 -0
- package/src/feedback/diagnostics.ts +152 -0
- package/src/feedback/redact.ts +54 -0
- package/src/feedback/screenshot.ts +45 -0
- package/src/feedback/types.ts +60 -0
- package/src/i18n/es.ts +1690 -0
- package/src/i18n/index.ts +159 -0
- package/src/i18n/ja.ts +1686 -0
- package/src/i18n/ko.ts +1684 -0
- package/src/i18n/languages.ts +101 -0
- package/src/i18n/react.ts +6 -0
- package/src/i18n/zh-cn.ts +1686 -0
- package/src/i18n/zh-tw.ts +1686 -0
- package/src/layout/desk-keys.ts +7 -0
- package/src/layout/desks.ts +334 -0
- package/src/layout/launch-layout.ts +147 -0
- package/src/layout/pane-follow.ts +47 -0
- package/src/layout/pane-layout-move.ts +264 -0
- package/src/layout/pane-manager/dock-tree.ts +302 -0
- package/src/layout/pane-manager/docking.ts +209 -0
- package/src/layout/pane-manager/floating-actions.ts +160 -0
- package/src/layout/pane-manager/floating.ts +124 -0
- package/src/layout/pane-manager/gridlock-inference.ts +126 -0
- package/src/layout/pane-manager/gridlock.ts +143 -0
- package/src/layout/pane-manager/hidden-panes.ts +160 -0
- package/src/layout/pane-manager/index.ts +71 -0
- package/src/layout/pane-manager/layout-state.ts +203 -0
- package/src/layout/pane-manager/queries.ts +82 -0
- package/src/layout/pane-manager/types.ts +24 -0
- package/src/layout/pane-state.ts +48 -0
- package/src/layout/selected-ticker.ts +74 -0
- package/src/layout/ticker-navigation.ts +128 -0
- package/src/layout-marketplace/api.ts +65 -0
- package/src/layout-marketplace/cloud.ts +183 -0
- package/src/layout-marketplace/gallery-desktop.tsx +476 -0
- package/src/layout-marketplace/gallery-terminal.tsx +371 -0
- package/src/layout-marketplace/gallery.tsx +776 -0
- package/src/layout-marketplace/geometry.ts +80 -0
- package/src/layout-marketplace/linked-sync.ts +109 -0
- package/src/layout-marketplace/linked.ts +140 -0
- package/src/layout-marketplace/mini-workspace.tsx +428 -0
- package/src/layout-marketplace/model.ts +270 -0
- package/src/layout-marketplace/pane-imagery.ts +87 -0
- package/src/layout-marketplace/pane.tsx +20 -0
- package/src/layout-marketplace/use-marketplace.ts +130 -0
- package/src/market-data/bars-per-year.ts +43 -0
- package/src/market-data/bond-funds.ts +85 -0
- package/src/market-data/commodity-names.ts +80 -0
- package/src/market-data/coordinator/auxiliary.ts +275 -0
- package/src/market-data/coordinator/chart.ts +164 -0
- package/src/market-data/coordinator/entries.ts +180 -0
- package/src/market-data/coordinator/events.ts +103 -0
- package/src/market-data/coordinator/financials.ts +260 -0
- package/src/market-data/coordinator/fx-legs.ts +71 -0
- package/src/market-data/coordinator/index.ts +854 -0
- package/src/market-data/coordinator/quotes.ts +485 -0
- package/src/market-data/depositary-pair.ts +251 -0
- package/src/market-data/frame-scheduler.ts +335 -0
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/futures-board.ts +255 -0
- package/src/market-data/futures-contract.ts +486 -0
- package/src/market-data/fx-closes.ts +164 -0
- package/src/market-data/history-session.ts +97 -0
- package/src/market-data/hooks.tsx +630 -0
- package/src/market-data/market/bar-period.ts +107 -0
- package/src/market-data/market/change-basis.ts +302 -0
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/format.ts +713 -0
- package/src/market-data/market/freshness.ts +794 -0
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/index-level.ts +51 -0
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/price-basis.ts +9 -0
- package/src/market-data/market/session-close-note.ts +58 -0
- package/src/market-data/market/status.ts +588 -0
- package/src/market-data/market/trading-sessions.ts +149 -0
- package/src/market-data/market/year-to-date.ts +41 -0
- package/src/market-data/model-dividend-yield.ts +117 -0
- package/src/market-data/options-alternatives.ts +206 -0
- package/src/market-data/performance.ts +125 -0
- package/src/market-data/perpetual-premium.ts +32 -0
- package/src/market-data/portfolio-financials.ts +37 -0
- package/src/market-data/published-apac-sessions.ts +99 -0
- package/src/market-data/published-cn-sessions.ts +25 -0
- package/src/market-data/published-futures-sessions.ts +69 -0
- package/src/market-data/published-jpx-sessions.ts +22 -0
- package/src/market-data/published-nse-sessions.ts +20 -0
- package/src/market-data/published-us-sessions.ts +87 -0
- package/src/market-data/query-store.ts +95 -0
- package/src/market-data/quote-subscription-target.ts +79 -0
- package/src/market-data/quotes/clock.ts +56 -0
- package/src/market-data/quotes/contributions.ts +336 -0
- package/src/market-data/quotes/day-range.ts +106 -0
- package/src/market-data/quotes/delisting.ts +34 -0
- package/src/market-data/quotes/freshness.ts +174 -0
- package/src/market-data/quotes/intraday-bar.ts +37 -0
- package/src/market-data/quotes/live-valuation.ts +235 -0
- package/src/market-data/quotes/metadata.ts +66 -0
- package/src/market-data/quotes/regular-session-valuation.ts +198 -0
- package/src/market-data/quotes/resolution.ts +653 -0
- package/src/market-data/quotes/screener-live-quotes.ts +283 -0
- package/src/market-data/quotes/time.ts +83 -0
- package/src/market-data/realized-volatility.ts +301 -0
- package/src/market-data/request-types.ts +127 -0
- package/src/market-data/result-types.ts +53 -0
- package/src/market-data/selectors.ts +131 -0
- package/src/market-data/snapshot-provider.ts +299 -0
- package/src/market-data/tax-equivalent.ts +352 -0
- package/src/market-data/ticker-price-returns.ts +20 -0
- package/src/market-data/trace.ts +7 -0
- package/src/news/aggregator.ts +689 -0
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +67 -0
- package/src/news/news-model.ts +247 -0
- package/src/news/source-label.ts +22 -0
- package/src/news/ticker-symbols.ts +21 -0
- package/src/news/types.ts +110 -0
- package/src/notifications/app-badge.ts +30 -0
- package/src/notifications/app-notifier.ts +287 -0
- package/src/notifications/notification-log.ts +186 -0
- package/src/pane-settings.ts +89 -0
- package/src/plugins/absorbed.ts +33 -0
- package/src/plugins/auto-update-node.ts +115 -0
- package/src/plugins/auto-update.ts +281 -0
- package/src/plugins/builtin/account-management/calendar-tab.tsx +196 -0
- package/src/plugins/builtin/account-management/footer.ts +37 -0
- package/src/plugins/builtin/account-management/form-components.tsx +199 -0
- package/src/plugins/builtin/account-management/index.tsx +28 -0
- package/src/plugins/builtin/account-management/keyboard.ts +102 -0
- package/src/plugins/builtin/account-management/model.ts +358 -0
- package/src/plugins/builtin/account-management/navigation.ts +9 -0
- package/src/plugins/builtin/account-management/pane.tsx +1100 -0
- package/src/plugins/builtin/account-management/password-dialog.tsx +127 -0
- package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +138 -0
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +429 -0
- package/src/plugins/builtin/alerts/catalysts.ts +77 -0
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +1134 -0
- package/src/plugins/builtin/alerts/command.ts +112 -0
- package/src/plugins/builtin/alerts/constants.ts +12 -0
- package/src/plugins/builtin/alerts/event-rows.ts +20 -0
- package/src/plugins/builtin/alerts/events-pane.tsx +255 -0
- package/src/plugins/builtin/alerts/events.ts +260 -0
- package/src/plugins/builtin/alerts/fired-log.ts +183 -0
- package/src/plugins/builtin/alerts/focus.ts +27 -0
- package/src/plugins/builtin/alerts/format.ts +146 -0
- package/src/plugins/builtin/alerts/headless.ts +266 -0
- package/src/plugins/builtin/alerts/history-pane.tsx +161 -0
- package/src/plugins/builtin/alerts/history-rows.ts +93 -0
- package/src/plugins/builtin/alerts/history.ts +91 -0
- package/src/plugins/builtin/alerts/index.tsx +520 -0
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +60 -0
- package/src/plugins/builtin/alerts/live.ts +136 -0
- 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 +484 -0
- package/src/plugins/builtin/alerts/price.ts +35 -0
- package/src/plugins/builtin/alerts/quotes.ts +98 -0
- package/src/plugins/builtin/alerts/research-builder.ts +160 -0
- package/src/plugins/builtin/alerts/research-rules.ts +316 -0
- package/src/plugins/builtin/alerts/scheduled-check.ts +296 -0
- package/src/plugins/builtin/alerts/scheduled-sources.ts +48 -0
- package/src/plugins/builtin/alerts/scheduled.ts +413 -0
- package/src/plugins/builtin/alerts/storage.ts +66 -0
- package/src/plugins/builtin/alerts/targets.ts +101 -0
- package/src/plugins/builtin/alerts/types.ts +31 -0
- package/src/plugins/builtin/analytics/account-returns.ts +109 -0
- package/src/plugins/builtin/analytics/broker-performance.ts +131 -0
- package/src/plugins/builtin/analytics/display.ts +15 -0
- package/src/plugins/builtin/analytics/index.tsx +502 -0
- package/src/plugins/builtin/analytics/metrics.ts +218 -0
- package/src/plugins/builtin/analytics/pane-model.ts +487 -0
- package/src/plugins/builtin/analytics/portfolio-selection.ts +29 -0
- package/src/plugins/builtin/analytics/risk-book.ts +215 -0
- package/src/plugins/builtin/analytics/risk-client.ts +711 -0
- package/src/plugins/builtin/analytics/risk-evidence.ts +485 -0
- package/src/plugins/builtin/analytics/risk-fx.ts +218 -0
- package/src/plugins/builtin/analytics/risk-headless.ts +413 -0
- package/src/plugins/builtin/analytics/risk-math.ts +489 -0
- package/src/plugins/builtin/analytics/risk-model.ts +1288 -0
- package/src/plugins/builtin/analytics/risk-options.ts +265 -0
- package/src/plugins/builtin/analytics/risk-pane.tsx +839 -0
- package/src/plugins/builtin/analytics/risk-test-data.ts +262 -0
- package/src/plugins/builtin/analytics/sector-model.ts +187 -0
- package/src/plugins/builtin/analytics/sharpe-cadence.ts +136 -0
- package/src/plugins/builtin/analytics/view.tsx +186 -0
- package/src/plugins/builtin/attention/client.ts +80 -0
- package/src/plugins/builtin/attention/evidence.ts +30 -0
- package/src/plugins/builtin/attention/headless.ts +44 -0
- package/src/plugins/builtin/attention/index.tsx +21 -0
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/attention/model.ts +81 -0
- package/src/plugins/builtin/attention/pane.tsx +219 -0
- package/src/plugins/builtin/attention/series.ts +26 -0
- package/src/plugins/builtin/attention/trending.tsx +61 -0
- package/src/plugins/builtin/awards/alerts.ts +32 -0
- package/src/plugins/builtin/awards/client.ts +82 -0
- package/src/plugins/builtin/awards/detail.tsx +107 -0
- package/src/plugins/builtin/awards/evidence.ts +36 -0
- package/src/plugins/builtin/awards/headless.ts +135 -0
- package/src/plugins/builtin/awards/index.tsx +22 -0
- package/src/plugins/builtin/awards/model.ts +100 -0
- package/src/plugins/builtin/awards/pane.tsx +286 -0
- package/src/plugins/builtin/awards/series.ts +29 -0
- package/src/plugins/builtin/backtest/client.ts +92 -0
- package/src/plugins/builtin/backtest/engine.ts +185 -0
- package/src/plugins/builtin/backtest/export.ts +27 -0
- package/src/plugins/builtin/backtest/format.ts +52 -0
- package/src/plugins/builtin/backtest/headless.ts +81 -0
- package/src/plugins/builtin/backtest/index.tsx +33 -0
- package/src/plugins/builtin/backtest/pane.tsx +301 -0
- package/src/plugins/builtin/backtest/presets.ts +60 -0
- package/src/plugins/builtin/backtest/report.ts +160 -0
- package/src/plugins/builtin/backtest/rules.ts +209 -0
- package/src/plugins/builtin/backtest/window.ts +125 -0
- package/src/plugins/builtin/bond-calculator/client.ts +28 -0
- package/src/plugins/builtin/bond-calculator/headless.ts +56 -0
- package/src/plugins/builtin/bond-calculator/index.tsx +10 -0
- package/src/plugins/builtin/bond-calculator/math.ts +164 -0
- package/src/plugins/builtin/bond-calculator/model.ts +64 -0
- package/src/plugins/builtin/bond-calculator/pane.tsx +160 -0
- package/src/plugins/builtin/broker-manager/add-flow.tsx +662 -0
- package/src/plugins/builtin/broker-manager/add-request.ts +34 -0
- package/src/plugins/builtin/broker-manager/detail.tsx +380 -0
- package/src/plugins/builtin/broker-manager/footer.ts +89 -0
- package/src/plugins/builtin/broker-manager/index.tsx +411 -0
- package/src/plugins/builtin/broker-manager/keyboard.ts +59 -0
- package/src/plugins/builtin/broker-manager/model.ts +181 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +364 -0
- package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
- package/src/plugins/builtin/broker-manager/table.ts +86 -0
- package/src/plugins/builtin/broker-trading/account-picker.tsx +92 -0
- package/src/plugins/builtin/broker-trading/chart-levels.ts +123 -0
- package/src/plugins/builtin/broker-trading/command.ts +103 -0
- package/src/plugins/builtin/broker-trading/controller.ts +730 -0
- package/src/plugins/builtin/broker-trading/dock.tsx +202 -0
- package/src/plugins/builtin/broker-trading/headless.ts +77 -0
- package/src/plugins/builtin/broker-trading/index.tsx +95 -0
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +456 -0
- package/src/plugins/builtin/broker-trading/orders-data.ts +133 -0
- package/src/plugins/builtin/broker-trading/orders.tsx +515 -0
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +127 -0
- package/src/plugins/builtin/broker-trading/pane-model.ts +235 -0
- package/src/plugins/builtin/broker-trading/pane.tsx +594 -0
- package/src/plugins/builtin/broker-trading/strategy.ts +153 -0
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +146 -0
- package/src/plugins/builtin/broker-trading/ticket.tsx +659 -0
- package/src/plugins/builtin/buildout/cells.tsx +206 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +243 -0
- package/src/plugins/builtin/buildout/detail/company.tsx +279 -0
- package/src/plugins/builtin/buildout/detail/index.tsx +82 -0
- package/src/plugins/builtin/buildout/detail/intel.tsx +56 -0
- package/src/plugins/builtin/buildout/detail/satellite-images.tsx +80 -0
- package/src/plugins/builtin/buildout/detail/site.tsx +115 -0
- package/src/plugins/builtin/buildout/detail/ui.tsx +235 -0
- package/src/plugins/builtin/buildout/detail/values.ts +67 -0
- package/src/plugins/builtin/buildout/format.ts +224 -0
- package/src/plugins/builtin/buildout/index.tsx +24 -0
- package/src/plugins/builtin/buildout/model/index.ts +144 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +431 -0
- package/src/plugins/builtin/buildout/model/types.ts +321 -0
- package/src/plugins/builtin/buildout/pane/index.tsx +470 -0
- package/src/plugins/builtin/buildout/pane/status.tsx +87 -0
- package/src/plugins/builtin/buildout/table-model.ts +452 -0
- package/src/plugins/builtin/builtin-plugin-meta.ts +165 -0
- package/src/plugins/builtin/catalysts/client.ts +28 -0
- package/src/plugins/builtin/catalysts/detail.tsx +89 -0
- package/src/plugins/builtin/catalysts/evidence.ts +47 -0
- package/src/plugins/builtin/catalysts/headless.ts +59 -0
- package/src/plugins/builtin/catalysts/history.ts +26 -0
- package/src/plugins/builtin/catalysts/index.tsx +31 -0
- package/src/plugins/builtin/catalysts/model.ts +280 -0
- package/src/plugins/builtin/catalysts/pane.tsx +163 -0
- package/src/plugins/builtin/cds/client.ts +169 -0
- package/src/plugins/builtin/cds/headless.ts +367 -0
- package/src/plugins/builtin/cds/index.tsx +39 -0
- package/src/plugins/builtin/cds/model.ts +815 -0
- package/src/plugins/builtin/cds/pane.tsx +547 -0
- package/src/plugins/builtin/cds/series.ts +19 -0
- package/src/plugins/builtin/central-bank-rates/client.ts +66 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +81 -0
- package/src/plugins/builtin/central-bank-rates/index.tsx +20 -0
- package/src/plugins/builtin/central-bank-rates/model.ts +151 -0
- package/src/plugins/builtin/central-bank-rates/pane.tsx +73 -0
- package/src/plugins/builtin/central-bank-rates/series.ts +36 -0
- package/src/plugins/builtin/changelog/index.tsx +313 -0
- package/src/plugins/builtin/changelog/model.ts +88 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +368 -0
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +580 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +159 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +131 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +323 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +657 -0
- package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +138 -0
- package/src/plugins/builtin/chart-composer/editor/model.ts +297 -0
- package/src/plugins/builtin/chart-composer/editor/opentui-view.tsx +111 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +252 -0
- package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
- package/src/plugins/builtin/chart-composer/follow-binding.ts +78 -0
- package/src/plugins/builtin/chart-composer/formula.ts +286 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +256 -0
- package/src/plugins/builtin/chart-composer/headless.ts +807 -0
- package/src/plugins/builtin/chart-composer/index.tsx +544 -0
- package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
- package/src/plugins/builtin/chart-composer/option-range.ts +39 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +1477 -0
- package/src/plugins/builtin/chart-composer/presets.ts +320 -0
- package/src/plugins/builtin/chart-composer/price-levels.ts +80 -0
- package/src/plugins/builtin/chart-composer/quick-add.tsx +488 -0
- package/src/plugins/builtin/chart-composer/semantic.ts +113 -0
- package/src/plugins/builtin/chart-composer/series-catalog.ts +473 -0
- package/src/plugins/builtin/chart-composer/series-expression.ts +274 -0
- package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
- package/src/plugins/builtin/chart-composer/session-history.ts +382 -0
- package/src/plugins/builtin/chart-composer/session-range.ts +89 -0
- package/src/plugins/builtin/chart-composer/settings.ts +527 -0
- package/src/plugins/builtin/chart-composer/shortcuts.ts +27 -0
- package/src/plugins/builtin/chart-composer/studies.ts +242 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +384 -0
- package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +93 -0
- package/src/plugins/builtin/chat/attachments/desktop.tsx +614 -0
- package/src/plugins/builtin/chat/attachments/files.ts +45 -0
- package/src/plugins/builtin/chat/attachments/model.ts +168 -0
- package/src/plugins/builtin/chat/attachments/terminal.tsx +72 -0
- package/src/plugins/builtin/chat/attachments/transfer.ts +165 -0
- package/src/plugins/builtin/chat/channel-labels.ts +67 -0
- package/src/plugins/builtin/chat/channels.ts +170 -0
- package/src/plugins/builtin/chat/command-bar.ts +136 -0
- package/src/plugins/builtin/chat/composer-commands.ts +54 -0
- package/src/plugins/builtin/chat/content/channel-navigation.ts +285 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
- package/src/plugins/builtin/chat/content/composer-cursor.ts +29 -0
- package/src/plugins/builtin/chat/content/composer-runtime.ts +471 -0
- package/src/plugins/builtin/chat/content/composer.tsx +310 -0
- package/src/plugins/builtin/chat/content/footer.tsx +321 -0
- package/src/plugins/builtin/chat/content/handle-prompt.tsx +326 -0
- package/src/plugins/builtin/chat/content/index.tsx +1289 -0
- package/src/plugins/builtin/chat/content/layout-metrics.ts +98 -0
- package/src/plugins/builtin/chat/content/mentions.ts +118 -0
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +431 -0
- package/src/plugins/builtin/chat/content/new-messages.tsx +89 -0
- package/src/plugins/builtin/chat/content/open-targets.ts +80 -0
- package/src/plugins/builtin/chat/content/row-activity.ts +132 -0
- package/src/plugins/builtin/chat/content/scroll.ts +396 -0
- package/src/plugins/builtin/chat/content/selection-runtime.ts +66 -0
- package/src/plugins/builtin/chat/content/shortcuts.ts +344 -0
- package/src/plugins/builtin/chat/content/snapshot.ts +202 -0
- package/src/plugins/builtin/chat/content/transcript.tsx +296 -0
- package/src/plugins/builtin/chat/content/types.ts +35 -0
- package/src/plugins/builtin/chat/content/user-map.ts +23 -0
- package/src/plugins/builtin/chat/content/whole-message-copy.ts +112 -0
- package/src/plugins/builtin/chat/controller/attachments.ts +213 -0
- package/src/plugins/builtin/chat/controller/channel-actions.ts +97 -0
- package/src/plugins/builtin/chat/controller/channels.ts +180 -0
- package/src/plugins/builtin/chat/controller/connections.ts +110 -0
- package/src/plugins/builtin/chat/controller/draft-sync.ts +41 -0
- package/src/plugins/builtin/chat/controller/fetch.ts +85 -0
- package/src/plugins/builtin/chat/controller/index.ts +980 -0
- package/src/plugins/builtin/chat/controller/lifecycle.ts +153 -0
- package/src/plugins/builtin/chat/controller/mention-candidates.ts +104 -0
- package/src/plugins/builtin/chat/controller/message-loading.ts +104 -0
- package/src/plugins/builtin/chat/controller/messages.ts +243 -0
- package/src/plugins/builtin/chat/controller/notifications.ts +153 -0
- package/src/plugins/builtin/chat/controller/persistence.ts +170 -0
- package/src/plugins/builtin/chat/controller/realtime.ts +169 -0
- package/src/plugins/builtin/chat/controller/send.ts +250 -0
- package/src/plugins/builtin/chat/controller/session-runtime.ts +245 -0
- package/src/plugins/builtin/chat/controller/session.ts +41 -0
- package/src/plugins/builtin/chat/controller/state.ts +221 -0
- package/src/plugins/builtin/chat/controller/storage.ts +134 -0
- package/src/plugins/builtin/chat/controller/unread-people.ts +240 -0
- package/src/plugins/builtin/chat/controller/utils.ts +65 -0
- package/src/plugins/builtin/chat/controller/view.ts +103 -0
- package/src/plugins/builtin/chat/conversation-recency.ts +73 -0
- package/src/plugins/builtin/chat/conversations.ts +263 -0
- package/src/plugins/builtin/chat/direct-messages.ts +200 -0
- package/src/plugins/builtin/chat/discord-command.ts +91 -0
- package/src/plugins/builtin/chat/edit-window.ts +30 -0
- package/src/plugins/builtin/chat/ghost-user.ts +42 -0
- package/src/plugins/builtin/chat/handle-prompt.ts +128 -0
- package/src/plugins/builtin/chat/index.tsx +229 -0
- package/src/plugins/builtin/chat/layout.ts +570 -0
- package/src/plugins/builtin/chat/markdown.ts +94 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +276 -0
- package/src/plugins/builtin/chat/message/header.tsx +209 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +315 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +349 -0
- package/src/plugins/builtin/chat/message/pending-send.ts +35 -0
- package/src/plugins/builtin/chat/message/person-icons.tsx +53 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +379 -0
- package/src/plugins/builtin/chat/message/reactions.tsx +210 -0
- package/src/plugins/builtin/chat/message/render-state.ts +151 -0
- package/src/plugins/builtin/chat/message/terminal.tsx +234 -0
- package/src/plugins/builtin/chat/message/types.ts +52 -0
- package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
- package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +158 -0
- package/src/plugins/builtin/chat/pane-links/open.ts +36 -0
- package/src/plugins/builtin/chat/pane-links/previews.ts +119 -0
- package/src/plugins/builtin/chat/pane-requests.ts +60 -0
- package/src/plugins/builtin/chat/pane-state.ts +115 -0
- package/src/plugins/builtin/chat/pane.tsx +145 -0
- package/src/plugins/builtin/chat/people.ts +202 -0
- package/src/plugins/builtin/chat/profile-popover.ts +207 -0
- package/src/plugins/builtin/chat/reactions.ts +197 -0
- package/src/plugins/builtin/chat/share/flow.tsx +32 -0
- package/src/plugins/builtin/chat/sidebar-rows.ts +109 -0
- package/src/plugins/builtin/chat/sidebar-store.ts +100 -0
- package/src/plugins/builtin/chat/sidebar.tsx +345 -0
- package/src/plugins/builtin/chat/status-widget.tsx +114 -0
- package/src/plugins/builtin/chat/unread-badge.ts +96 -0
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +281 -0
- package/src/plugins/builtin/chat/unread-inbox.ts +113 -0
- package/src/plugins/builtin/clinical-trials/client.ts +265 -0
- package/src/plugins/builtin/clinical-trials/index.tsx +83 -0
- package/src/plugins/builtin/clinical-trials/model.ts +269 -0
- package/src/plugins/builtin/clinical-trials/pane.tsx +423 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +158 -0
- package/src/plugins/builtin/clinical-trials/types.ts +2 -0
- package/src/plugins/builtin/cloud/askg/attached-pane.tsx +69 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +714 -0
- package/src/plugins/builtin/cloud/askg/conversation-store.ts +243 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +476 -0
- package/src/plugins/builtin/cloud/askg/host.ts +94 -0
- package/src/plugins/builtin/cloud/askg/json.ts +31 -0
- package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +370 -0
- package/src/plugins/builtin/cloud/askg/model.ts +854 -0
- package/src/plugins/builtin/cloud/askg/notes.ts +100 -0
- package/src/plugins/builtin/cloud/askg/pane-attachment.ts +93 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +1749 -0
- package/src/plugins/builtin/cloud/askg/pending-question.ts +27 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +377 -0
- package/src/plugins/builtin/cloud/askg/result-budget.ts +156 -0
- package/src/plugins/builtin/cloud/askg/sidebar.tsx +184 -0
- package/src/plugins/builtin/cloud/askg/tool-display.ts +440 -0
- package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +404 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +254 -0
- package/src/plugins/builtin/cloud/askg/user-data.ts +89 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +158 -0
- package/src/plugins/builtin/cloud/auth-commands.ts +103 -0
- package/src/plugins/builtin/cloud/browser.tsx +3 -0
- package/src/plugins/builtin/cloud/company-picker.tsx +645 -0
- package/src/plugins/builtin/cloud/index.tsx +4 -0
- package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +366 -0
- package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
- package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
- package/src/plugins/builtin/cloud/plugin.tsx +203 -0
- package/src/plugins/builtin/cloud/team/acm-tab.tsx +127 -0
- package/src/plugins/builtin/cloud/team/capability.ts +71 -0
- package/src/plugins/builtin/cloud/team/command.ts +313 -0
- package/src/plugins/builtin/cloud/team/module.tsx +81 -0
- package/src/plugins/builtin/cloud/team/pane-model.ts +185 -0
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +576 -0
- package/src/plugins/builtin/cloud/team/pane-ui.tsx +362 -0
- package/src/plugins/builtin/cloud/team/pane.tsx +940 -0
- package/src/plugins/builtin/cloud/team/status-widget.tsx +78 -0
- package/src/plugins/builtin/cloud/team/team-state-host.ts +70 -0
- package/src/plugins/builtin/cloud/team/views.ts +192 -0
- package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
- package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
- package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +739 -0
- package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
- package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
- package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +250 -0
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +162 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +193 -0
- package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
- package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
- package/src/plugins/builtin/cloud/thesis/board-pane.tsx +484 -0
- package/src/plugins/builtin/cloud/thesis/detail.tsx +539 -0
- package/src/plugins/builtin/cloud/thesis/exposure.ts +145 -0
- package/src/plugins/builtin/cloud/thesis/flows.ts +553 -0
- package/src/plugins/builtin/cloud/thesis/model.ts +399 -0
- package/src/plugins/builtin/cloud/thesis/module.tsx +65 -0
- package/src/plugins/builtin/cloud/thesis/pane-request.ts +30 -0
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +104 -0
- package/src/plugins/builtin/cloud/thesis/store.ts +302 -0
- package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +140 -0
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +203 -0
- package/src/plugins/builtin/cloud/upgrade-command.ts +35 -0
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +45 -0
- package/src/plugins/builtin/cloud/verification-status-widget.tsx +75 -0
- package/src/plugins/builtin/cloud-tweets/footer.ts +40 -0
- package/src/plugins/builtin/cloud-tweets/index.ts +6 -0
- package/src/plugins/builtin/cloud-tweets/keyboard.ts +34 -0
- package/src/plugins/builtin/cloud-tweets/model.ts +280 -0
- package/src/plugins/builtin/cloud-tweets/pane.tsx +327 -0
- package/src/plugins/builtin/cloud-tweets/registration.ts +130 -0
- package/src/plugins/builtin/cloud-tweets/search-bar.tsx +38 -0
- package/src/plugins/builtin/cloud-tweets/table.tsx +551 -0
- package/src/plugins/builtin/cloud-tweets/tweet-text.ts +18 -0
- package/src/plugins/builtin/comment-letters/client.ts +340 -0
- package/src/plugins/builtin/comment-letters/index.tsx +75 -0
- package/src/plugins/builtin/comment-letters/pane.tsx +314 -0
- package/src/plugins/builtin/comment-letters/types.ts +43 -0
- package/src/plugins/builtin/commodities-energy/index.tsx +228 -0
- package/src/plugins/builtin/commodities-energy/view-state.ts +13 -0
- package/src/plugins/builtin/commodity-balances/client.ts +68 -0
- package/src/plugins/builtin/commodity-balances/headless.ts +107 -0
- package/src/plugins/builtin/commodity-balances/index.tsx +47 -0
- package/src/plugins/builtin/commodity-balances/model.ts +108 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +267 -0
- package/src/plugins/builtin/company-attention/app-rank.tsx +80 -0
- package/src/plugins/builtin/company-attention/building-model.ts +222 -0
- package/src/plugins/builtin/company-attention/building.tsx +176 -0
- package/src/plugins/builtin/company-attention/client.ts +184 -0
- package/src/plugins/builtin/company-attention/evidence.ts +61 -0
- package/src/plugins/builtin/company-attention/headless.ts +172 -0
- package/src/plugins/builtin/company-attention/index.tsx +35 -0
- package/src/plugins/builtin/company-attention/model.ts +270 -0
- package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
- package/src/plugins/builtin/company-attention/moves.tsx +163 -0
- package/src/plugins/builtin/company-attention/pane.tsx +253 -0
- package/src/plugins/builtin/company-attention/series.ts +52 -0
- package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
- package/src/plugins/builtin/company-attention/signals.tsx +164 -0
- package/src/plugins/builtin/company-kpis/client.ts +108 -0
- package/src/plugins/builtin/company-kpis/headless.ts +122 -0
- package/src/plugins/builtin/company-kpis/index.tsx +26 -0
- package/src/plugins/builtin/company-kpis/model.ts +229 -0
- package/src/plugins/builtin/company-kpis/pane.tsx +256 -0
- package/src/plugins/builtin/company-kpis/related.ts +11 -0
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +51 -0
- package/src/plugins/builtin/composite-plugins.ts +284 -0
- package/src/plugins/builtin/congress-trades/aggregates.ts +69 -0
- package/src/plugins/builtin/congress-trades/client.ts +13 -0
- package/src/plugins/builtin/congress-trades/detail.tsx +340 -0
- package/src/plugins/builtin/congress-trades/filters.tsx +36 -0
- package/src/plugins/builtin/congress-trades/footer.ts +86 -0
- package/src/plugins/builtin/congress-trades/headless.ts +212 -0
- package/src/plugins/builtin/congress-trades/index.tsx +26 -0
- package/src/plugins/builtin/congress-trades/keyboard.ts +45 -0
- package/src/plugins/builtin/congress-trades/model.ts +399 -0
- package/src/plugins/builtin/congress-trades/pane.tsx +519 -0
- package/src/plugins/builtin/congress-trades/table.tsx +102 -0
- package/src/plugins/builtin/connections/index.ts +58 -0
- package/src/plugins/builtin/connections/pane.tsx +243 -0
- package/src/plugins/builtin/correlation/compute.ts +139 -0
- package/src/plugins/builtin/correlation/geo.ts +34 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +73 -0
- package/src/plugins/builtin/correlation/headless.ts +158 -0
- package/src/plugins/builtin/correlation/history.ts +43 -0
- package/src/plugins/builtin/correlation/index.tsx +490 -0
- package/src/plugins/builtin/correlation/matrix/model.ts +199 -0
- package/src/plugins/builtin/correlation/matrix/pair-chart.tsx +48 -0
- package/src/plugins/builtin/correlation/matrix/selection.ts +79 -0
- package/src/plugins/builtin/correlation/matrix/symbol-cell.tsx +57 -0
- package/src/plugins/builtin/correlation/relationship/history.ts +46 -0
- package/src/plugins/builtin/correlation/relationship/model.ts +267 -0
- package/src/plugins/builtin/correlation/relationship/pane.tsx +354 -0
- package/src/plugins/builtin/correlation/relationship/view-model.ts +123 -0
- package/src/plugins/builtin/correlation/settings.ts +95 -0
- package/src/plugins/builtin/cot/client.ts +111 -0
- package/src/plugins/builtin/cot/headless.ts +96 -0
- package/src/plugins/builtin/cot/index.tsx +28 -0
- package/src/plugins/builtin/cot/model.ts +277 -0
- package/src/plugins/builtin/cot/pane.tsx +200 -0
- package/src/plugins/builtin/cot/series.ts +27 -0
- package/src/plugins/builtin/cpi/client.ts +66 -0
- package/src/plugins/builtin/cpi/headless.ts +86 -0
- package/src/plugins/builtin/cpi/index.tsx +31 -0
- package/src/plugins/builtin/cpi/model.ts +198 -0
- package/src/plugins/builtin/cpi/pane.tsx +160 -0
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +178 -0
- package/src/plugins/builtin/credit-boards/client.ts +115 -0
- package/src/plugins/builtin/credit-boards/headless.ts +218 -0
- package/src/plugins/builtin/credit-boards/index.tsx +54 -0
- package/src/plugins/builtin/credit-boards/model.ts +323 -0
- package/src/plugins/builtin/credit-boards/series.ts +66 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +156 -0
- package/src/plugins/builtin/credit-conditions/client.ts +121 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +74 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +163 -0
- package/src/plugins/builtin/credit-conditions/model.ts +98 -0
- package/src/plugins/builtin/credit-conditions/series.ts +18 -0
- package/src/plugins/builtin/credit-documents/client.ts +99 -0
- package/src/plugins/builtin/credit-documents/detail.tsx +110 -0
- package/src/plugins/builtin/credit-documents/evidence.ts +64 -0
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/gauge.tsx +52 -0
- package/src/plugins/builtin/credit-documents/headless.ts +72 -0
- package/src/plugins/builtin/credit-documents/index.tsx +39 -0
- package/src/plugins/builtin/credit-documents/model.ts +419 -0
- package/src/plugins/builtin/credit-documents/pane.tsx +195 -0
- package/src/plugins/builtin/crypto-board/client.ts +369 -0
- package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
- package/src/plugins/builtin/crypto-board/defi.ts +239 -0
- package/src/plugins/builtin/crypto-board/headless.ts +304 -0
- package/src/plugins/builtin/crypto-board/index.tsx +49 -0
- package/src/plugins/builtin/crypto-board/model.ts +521 -0
- package/src/plugins/builtin/crypto-board/pane.tsx +419 -0
- package/src/plugins/builtin/crypto-board/series.ts +117 -0
- package/src/plugins/builtin/crypto-board/supply.ts +123 -0
- package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
- package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
- package/src/plugins/builtin/custom-view/index.tsx +69 -0
- package/src/plugins/builtin/custom-view/live-quotes.ts +143 -0
- package/src/plugins/builtin/custom-view/loader.ts +158 -0
- package/src/plugins/builtin/custom-view/pane.tsx +415 -0
- package/src/plugins/builtin/custom-view/sources.ts +148 -0
- package/src/plugins/builtin/custom-view/view-spec.ts +462 -0
- package/src/plugins/builtin/debt-maturities/client.ts +438 -0
- package/src/plugins/builtin/debt-maturities/headless.ts +125 -0
- package/src/plugins/builtin/debt-maturities/index.tsx +33 -0
- package/src/plugins/builtin/debt-maturities/model.ts +231 -0
- package/src/plugins/builtin/debt-maturities/pane.tsx +692 -0
- package/src/plugins/builtin/debt-maturities/series.ts +45 -0
- package/src/plugins/builtin/debt-maturities/wall-bar.tsx +36 -0
- package/src/plugins/builtin/debug/index.tsx +336 -0
- package/src/plugins/builtin/debug/template.ts +8 -0
- package/src/plugins/builtin/dividend-yield/browser.tsx +3 -0
- package/src/plugins/builtin/dividend-yield/client.ts +331 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +340 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +85 -0
- package/src/plugins/builtin/dividend-yield/model.ts +53 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +332 -0
- package/src/plugins/builtin/dividend-yield/provider-client.ts +52 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +210 -0
- package/src/plugins/builtin/dividend-yield/reference-price.ts +50 -0
- package/src/plugins/builtin/dividend-yield/series.ts +20 -0
- package/src/plugins/builtin/dividend-yield/types.ts +49 -0
- package/src/plugins/builtin/dividend-yield/view.ts +92 -0
- package/src/plugins/builtin/docs/blocks.tsx +239 -0
- package/src/plugins/builtin/docs/index.tsx +70 -0
- package/src/plugins/builtin/docs/pane.tsx +447 -0
- package/src/plugins/builtin/docs/targets.ts +78 -0
- package/src/plugins/builtin/docs/use-docs.ts +60 -0
- package/src/plugins/builtin/doe/client.ts +77 -0
- package/src/plugins/builtin/doe/headless.ts +110 -0
- package/src/plugins/builtin/doe/index.tsx +50 -0
- package/src/plugins/builtin/doe/model.ts +252 -0
- package/src/plugins/builtin/doe/pane.tsx +156 -0
- package/src/plugins/builtin/doe/prices.ts +84 -0
- package/src/plugins/builtin/doe/range-bar.tsx +15 -0
- package/src/plugins/builtin/doe/series.ts +65 -0
- package/src/plugins/builtin/earnings/board-model.ts +252 -0
- package/src/plugins/builtin/earnings/board-table.ts +147 -0
- package/src/plugins/builtin/earnings/board.tsx +466 -0
- package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
- package/src/plugins/builtin/earnings/calendar.tsx +338 -0
- package/src/plugins/builtin/earnings/client.ts +99 -0
- package/src/plugins/builtin/earnings/data/cache.ts +84 -0
- package/src/plugins/builtin/earnings/format.ts +58 -0
- package/src/plugins/builtin/earnings/headless.ts +339 -0
- package/src/plugins/builtin/earnings/history-model.ts +213 -0
- package/src/plugins/builtin/earnings/history-table.ts +106 -0
- package/src/plugins/builtin/earnings/history.tsx +141 -0
- package/src/plugins/builtin/earnings/index.tsx +158 -0
- package/src/plugins/builtin/earnings/model.ts +22 -0
- package/src/plugins/builtin/earnings/no-earnings.ts +52 -0
- package/src/plugins/builtin/earnings/scope.ts +77 -0
- package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
- package/src/plugins/builtin/earnings-calls/data.ts +515 -0
- package/src/plugins/builtin/earnings-calls/format.ts +72 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +461 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +82 -0
- package/src/plugins/builtin/earnings-calls/lock-note.ts +26 -0
- package/src/plugins/builtin/earnings-calls/model.ts +341 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +1125 -0
- package/src/plugins/builtin/earnings-calls/prose.ts +62 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +457 -0
- package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
- package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
- package/src/plugins/builtin/earnings-ripple/client.ts +133 -0
- package/src/plugins/builtin/earnings-ripple/headless.ts +55 -0
- package/src/plugins/builtin/earnings-ripple/index.tsx +37 -0
- package/src/plugins/builtin/earnings-ripple/model.ts +188 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +233 -0
- package/src/plugins/builtin/econ/calendar-model.ts +409 -0
- package/src/plugins/builtin/econ/calendar-source.ts +49 -0
- package/src/plugins/builtin/econ/detail-view.tsx +324 -0
- package/src/plugins/builtin/econ/figures.ts +72 -0
- package/src/plugins/builtin/econ/fred-catalog.ts +219 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +103 -0
- package/src/plugins/builtin/econ/index.tsx +612 -0
- package/src/plugins/builtin/econ/print-watch.ts +280 -0
- package/src/plugins/builtin/econ/types.ts +49 -0
- package/src/plugins/builtin/econ-statistics/cache.ts +8 -0
- package/src/plugins/builtin/econ-statistics/client.ts +153 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +100 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +85 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +268 -0
- package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
- package/src/plugins/builtin/econ-statistics/pane.tsx +219 -0
- package/src/plugins/builtin/econ-statistics/stats.ts +678 -0
- package/src/plugins/builtin/econ-statistics/view.ts +182 -0
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +103 -0
- package/src/plugins/builtin/equity-screener/criterion-editor.tsx +174 -0
- package/src/plugins/builtin/equity-screener/headless.ts +415 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/index.tsx +38 -0
- package/src/plugins/builtin/equity-screener/live.ts +149 -0
- package/src/plugins/builtin/equity-screener/model.ts +728 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +1127 -0
- package/src/plugins/builtin/equity-screener/results.ts +135 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
- package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
- package/src/plugins/builtin/estimate-revisions/client.ts +283 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +108 -0
- package/src/plugins/builtin/estimate-revisions/index.tsx +92 -0
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +45 -0
- package/src/plugins/builtin/estimate-revisions/model.ts +108 -0
- package/src/plugins/builtin/estimate-revisions/pane.tsx +601 -0
- package/src/plugins/builtin/estimate-revisions/sorting.ts +111 -0
- package/src/plugins/builtin/executives/data.ts +148 -0
- package/src/plugins/builtin/executives/format.ts +44 -0
- package/src/plugins/builtin/executives/index.tsx +68 -0
- package/src/plugins/builtin/executives/pane.tsx +448 -0
- package/src/plugins/builtin/exposure/charts.tsx +67 -0
- package/src/plugins/builtin/exposure/client.ts +56 -0
- package/src/plugins/builtin/exposure/drivers.ts +42 -0
- package/src/plugins/builtin/exposure/editor.tsx +61 -0
- package/src/plugins/builtin/exposure/evidence.ts +42 -0
- package/src/plugins/builtin/exposure/headless.ts +290 -0
- package/src/plugins/builtin/exposure/holdings.ts +58 -0
- package/src/plugins/builtin/exposure/index.tsx +40 -0
- package/src/plugins/builtin/exposure/model.ts +143 -0
- package/src/plugins/builtin/exposure/pane.tsx +276 -0
- package/src/plugins/builtin/fear-greed/cache.ts +100 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +262 -0
- package/src/plugins/builtin/fear-greed/data.ts +376 -0
- package/src/plugins/builtin/fear-greed/format.ts +87 -0
- package/src/plugins/builtin/fear-greed/headless.ts +87 -0
- package/src/plugins/builtin/fear-greed/index.tsx +104 -0
- package/src/plugins/builtin/fear-greed/pane.tsx +105 -0
- package/src/plugins/builtin/fear-greed/series.ts +52 -0
- package/src/plugins/builtin/filing-events/feed.ts +169 -0
- package/src/plugins/builtin/filing-events/index.tsx +79 -0
- package/src/plugins/builtin/filing-events/pane.tsx +312 -0
- package/src/plugins/builtin/fixed-income/index.tsx +46 -0
- package/src/plugins/builtin/fixed-income/model.ts +27 -0
- package/src/plugins/builtin/fixed-income/pane.tsx +164 -0
- package/src/plugins/builtin/fixed-income/shortcut.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +436 -0
- package/src/plugins/builtin/futures/model.ts +257 -0
- package/src/plugins/builtin/futures/returns.ts +104 -0
- package/src/plugins/builtin/futures/series.ts +71 -0
- package/src/plugins/builtin/futures/table.tsx +274 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +103 -0
- package/src/plugins/builtin/futures/use-front-returns.ts +137 -0
- package/src/plugins/builtin/futures-curve/client.ts +136 -0
- package/src/plugins/builtin/futures-curve/headless.ts +174 -0
- package/src/plugins/builtin/futures-curve/index.tsx +33 -0
- package/src/plugins/builtin/futures-curve/model.ts +769 -0
- package/src/plugins/builtin/futures-curve/pane.tsx +332 -0
- package/src/plugins/builtin/fx-matrix/client.ts +78 -0
- package/src/plugins/builtin/fx-matrix/direction.ts +81 -0
- package/src/plugins/builtin/fx-matrix/export.ts +33 -0
- package/src/plugins/builtin/fx-matrix/headless.ts +175 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +304 -0
- package/src/plugins/builtin/fx-matrix/live-legs.ts +167 -0
- package/src/plugins/builtin/fx-matrix/pairs.ts +81 -0
- package/src/plugins/builtin/fx-matrix/settings.ts +103 -0
- package/src/plugins/builtin/gpu/board.tsx +169 -0
- package/src/plugins/builtin/gpu/changes.tsx +94 -0
- package/src/plugins/builtin/gpu/client.ts +148 -0
- package/src/plugins/builtin/gpu/equities.tsx +122 -0
- package/src/plugins/builtin/gpu/headless.ts +119 -0
- package/src/plugins/builtin/gpu/history.tsx +142 -0
- package/src/plugins/builtin/gpu/index.tsx +20 -0
- package/src/plugins/builtin/gpu/ladder.tsx +123 -0
- package/src/plugins/builtin/gpu/model.ts +437 -0
- package/src/plugins/builtin/gpu/pane.tsx +81 -0
- package/src/plugins/builtin/gpu/series.ts +38 -0
- package/src/plugins/builtin/help/feedback-tab.tsx +98 -0
- package/src/plugins/builtin/help/function-card.tsx +221 -0
- package/src/plugins/builtin/help/function-index.ts +198 -0
- package/src/plugins/builtin/help/functions-table.tsx +188 -0
- package/src/plugins/builtin/help/index.tsx +432 -0
- package/src/plugins/builtin/help/keybindings-editor.tsx +437 -0
- package/src/plugins/builtin/help/shortcut-model.ts +116 -0
- package/src/plugins/builtin/help/shortcut-table.tsx +130 -0
- package/src/plugins/builtin/help/table-cells.tsx +66 -0
- package/src/plugins/builtin/holders/beneficial-client.ts +231 -0
- package/src/plugins/builtin/holders/beneficial-model.ts +363 -0
- package/src/plugins/builtin/holders/beneficial-report.ts +179 -0
- package/src/plugins/builtin/holders/beneficial-view.tsx +293 -0
- package/src/plugins/builtin/holders/client.ts +58 -0
- package/src/plugins/builtin/holders/format.ts +100 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +302 -0
- package/src/plugins/builtin/holders/index-holders.ts +264 -0
- package/src/plugins/builtin/holders/index.tsx +52 -0
- package/src/plugins/builtin/holders/pane.tsx +439 -0
- package/src/plugins/builtin/holders/report-header.ts +171 -0
- package/src/plugins/builtin/holders/report-rows.ts +268 -0
- package/src/plugins/builtin/holders/table-model.ts +86 -0
- package/src/plugins/builtin/holders/thirteenf-match.ts +117 -0
- package/src/plugins/builtin/holders/treemap.tsx +51 -0
- package/src/plugins/builtin/holders/types.ts +17 -0
- package/src/plugins/builtin/insider/amendments.ts +65 -0
- package/src/plugins/builtin/insider/base-values.ts +76 -0
- package/src/plugins/builtin/insider/client.ts +64 -0
- package/src/plugins/builtin/insider/detail.tsx +127 -0
- package/src/plugins/builtin/insider/display.ts +221 -0
- package/src/plugins/builtin/insider/headless.ts +574 -0
- package/src/plugins/builtin/insider/index.tsx +771 -0
- package/src/plugins/builtin/insider/insider-data.ts +176 -0
- package/src/plugins/builtin/insider/insiders.tsx +224 -0
- package/src/plugins/builtin/insider/issuer.ts +79 -0
- package/src/plugins/builtin/insider/model.ts +207 -0
- package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
- package/src/plugins/builtin/insider/signals-client.ts +193 -0
- package/src/plugins/builtin/insider/signals-model.ts +718 -0
- package/src/plugins/builtin/insider/signals.tsx +376 -0
- package/src/plugins/builtin/insider/table-model.ts +248 -0
- package/src/plugins/builtin/insider/table.tsx +82 -0
- package/src/plugins/builtin/ipo-calendar/client.ts +93 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +170 -0
- package/src/plugins/builtin/ipo-calendar/index.tsx +67 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +389 -0
- package/src/plugins/builtin/ipo-calendar/pane.tsx +248 -0
- package/src/plugins/builtin/iv-history/charts.tsx +64 -0
- package/src/plugins/builtin/iv-history/client.ts +254 -0
- package/src/plugins/builtin/iv-history/events.ts +145 -0
- package/src/plugins/builtin/iv-history/format.ts +77 -0
- package/src/plugins/builtin/iv-history/headless.ts +257 -0
- package/src/plugins/builtin/iv-history/index.tsx +50 -0
- package/src/plugins/builtin/iv-history/model.ts +532 -0
- package/src/plugins/builtin/iv-history/pane.tsx +141 -0
- package/src/plugins/builtin/iv-history/rank.ts +10 -0
- package/src/plugins/builtin/iv-history/screen-pane.tsx +193 -0
- package/src/plugins/builtin/iv-history/series.ts +31 -0
- package/src/plugins/builtin/iv-history/universe.ts +44 -0
- package/src/plugins/builtin/jobs/client.ts +81 -0
- package/src/plugins/builtin/jobs/headless.ts +221 -0
- package/src/plugins/builtin/jobs/index.tsx +88 -0
- package/src/plugins/builtin/jobs/lock-note.ts +27 -0
- package/src/plugins/builtin/jobs/model.ts +332 -0
- package/src/plugins/builtin/jobs/pages.ts +48 -0
- package/src/plugins/builtin/jobs/pane.tsx +781 -0
- package/src/plugins/builtin/jobs/series.ts +19 -0
- package/src/plugins/builtin/jobs/share-bars.tsx +76 -0
- package/src/plugins/builtin/jodi/client.ts +57 -0
- package/src/plugins/builtin/jodi/headless.ts +74 -0
- package/src/plugins/builtin/jodi/index.tsx +32 -0
- package/src/plugins/builtin/jodi/model.ts +50 -0
- package/src/plugins/builtin/jodi/pane.tsx +152 -0
- package/src/plugins/builtin/kelly-sizer/constants.ts +3 -0
- package/src/plugins/builtin/kelly-sizer/fields.ts +195 -0
- package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
- package/src/plugins/builtin/kelly-sizer/history.ts +82 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +49 -0
- package/src/plugins/builtin/kelly-sizer/model.ts +566 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +650 -0
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +55 -0
- package/src/plugins/builtin/kelly-sizer/sections.tsx +215 -0
- package/src/plugins/builtin/kelly-sizer/state.ts +67 -0
- package/src/plugins/builtin/kelly-sizer/test-support.tsx +165 -0
- package/src/plugins/builtin/kelly-sizer/types.ts +170 -0
- package/src/plugins/builtin/kelly-sizer/view.tsx +22 -0
- package/src/plugins/builtin/layout-manager/index.tsx +306 -0
- package/src/plugins/builtin/macro-day/client.ts +115 -0
- package/src/plugins/builtin/macro-day/headless.ts +72 -0
- package/src/plugins/builtin/macro-day/index.tsx +25 -0
- package/src/plugins/builtin/macro-day/model.ts +209 -0
- package/src/plugins/builtin/macro-day/pane.tsx +170 -0
- package/src/plugins/builtin/macro-day/release-table.ts +40 -0
- package/src/plugins/builtin/macro-day/releases.ts +118 -0
- package/src/plugins/builtin/macro-resources.ts +15 -0
- package/src/plugins/builtin/market-halts/client.ts +114 -0
- package/src/plugins/builtin/market-halts/index.tsx +42 -0
- package/src/plugins/builtin/market-halts/model.ts +361 -0
- package/src/plugins/builtin/market-halts/pane.tsx +240 -0
- package/src/plugins/builtin/market-heatmap/data.ts +92 -0
- package/src/plugins/builtin/market-heatmap/evidence.ts +99 -0
- package/src/plugins/builtin/market-heatmap/headless.ts +154 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +845 -0
- package/src/plugins/builtin/market-heatmap/live.ts +89 -0
- package/src/plugins/builtin/market-heatmap/model.ts +248 -0
- package/src/plugins/builtin/market-heatmap/portfolio.ts +317 -0
- package/src/plugins/builtin/market-movers/client.ts +165 -0
- package/src/plugins/builtin/market-movers/footer.ts +19 -0
- package/src/plugins/builtin/market-movers/headless.ts +208 -0
- package/src/plugins/builtin/market-movers/index.tsx +392 -0
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +373 -0
- package/src/plugins/builtin/market-movers/screener.ts +463 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +263 -0
- package/src/plugins/builtin/market-movers/session-table.ts +99 -0
- package/src/plugins/builtin/market-movers/session.ts +248 -0
- package/src/plugins/builtin/market-movers/table.ts +137 -0
- package/src/plugins/builtin/market-valuation/align.ts +146 -0
- package/src/plugins/builtin/market-valuation/cache.ts +3 -0
- package/src/plugins/builtin/market-valuation/chart-projection.ts +196 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +59 -0
- package/src/plugins/builtin/market-valuation/client.ts +158 -0
- package/src/plugins/builtin/market-valuation/defs.ts +253 -0
- package/src/plugins/builtin/market-valuation/detail.tsx +93 -0
- package/src/plugins/builtin/market-valuation/headless.ts +172 -0
- package/src/plugins/builtin/market-valuation/index.tsx +83 -0
- package/src/plugins/builtin/market-valuation/indicators.ts +386 -0
- package/src/plugins/builtin/market-valuation/pane.tsx +197 -0
- package/src/plugins/builtin/market-valuation/series.ts +29 -0
- package/src/plugins/builtin/market-valuation/settings.ts +38 -0
- package/src/plugins/builtin/market-valuation/sources.ts +192 -0
- package/src/plugins/builtin/market-valuation/view.ts +154 -0
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +97 -0
- package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
- package/src/plugins/builtin/market-valuation/zone-scale/model.ts +67 -0
- package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
- package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
- package/src/plugins/builtin/members/client.ts +68 -0
- package/src/plugins/builtin/members/headless.ts +54 -0
- package/src/plugins/builtin/members/index.tsx +20 -0
- package/src/plugins/builtin/members/model.ts +115 -0
- package/src/plugins/builtin/members/pane.tsx +205 -0
- package/src/plugins/builtin/mna/client.ts +154 -0
- package/src/plugins/builtin/mna/detail.tsx +285 -0
- package/src/plugins/builtin/mna/distress/client.ts +326 -0
- package/src/plugins/builtin/mna/distress/detail.tsx +259 -0
- package/src/plugins/builtin/mna/distress/headless.ts +259 -0
- package/src/plugins/builtin/mna/distress/model.ts +479 -0
- package/src/plugins/builtin/mna/distress/pane.tsx +614 -0
- package/src/plugins/builtin/mna/headless.ts +100 -0
- package/src/plugins/builtin/mna/index.tsx +74 -0
- package/src/plugins/builtin/mna/model.ts +282 -0
- package/src/plugins/builtin/mna/pane.tsx +477 -0
- package/src/plugins/builtin/money-markets/client.ts +54 -0
- package/src/plugins/builtin/money-markets/headless.ts +29 -0
- package/src/plugins/builtin/money-markets/index.tsx +20 -0
- package/src/plugins/builtin/money-markets/model.ts +55 -0
- package/src/plugins/builtin/money-markets/pane.tsx +142 -0
- package/src/plugins/builtin/money-markets/series.ts +41 -0
- package/src/plugins/builtin/news/headless.ts +233 -0
- package/src/plugins/builtin/news/index.tsx +234 -0
- package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
- package/src/plugins/builtin/news/wire/breaking/notifications.ts +202 -0
- package/src/plugins/builtin/news/wire/categories.ts +188 -0
- package/src/plugins/builtin/news/wire/default-feeds.ts +43 -0
- package/src/plugins/builtin/news/wire/feed-config.ts +251 -0
- package/src/plugins/builtin/news/wire/filter-articles.ts +54 -0
- package/src/plugins/builtin/news/wire/hash.ts +9 -0
- package/src/plugins/builtin/news/wire/index.tsx +252 -0
- package/src/plugins/builtin/news/wire/industry-pane.tsx +184 -0
- package/src/plugins/builtin/news/wire/letters/client.ts +109 -0
- package/src/plugins/builtin/news/wire/letters/model.ts +69 -0
- package/src/plugins/builtin/news/wire/letters/pane.tsx +155 -0
- package/src/plugins/builtin/news/wire/mutes.ts +254 -0
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +136 -0
- package/src/plugins/builtin/news/wire/news/detail-view.tsx +329 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +104 -0
- package/src/plugins/builtin/news/wire/news/list-search.ts +34 -0
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +158 -0
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +220 -0
- package/src/plugins/builtin/news/wire/news/query-presets.ts +126 -0
- package/src/plugins/builtin/news/wire/news/table.tsx +422 -0
- package/src/plugins/builtin/news/wire/persisted-articles.ts +123 -0
- package/src/plugins/builtin/news/wire/read-state.ts +29 -0
- package/src/plugins/builtin/news/wire/rss/cli.ts +48 -0
- package/src/plugins/builtin/news/wire/rss/feed-xml.ts +109 -0
- package/src/plugins/builtin/news/wire/rss/managed-feed.ts +20 -0
- package/src/plugins/builtin/news/wire/rss/parser.ts +182 -0
- package/src/plugins/builtin/news/wire/rss/source.ts +191 -0
- package/src/plugins/builtin/notes/cli.ts +172 -0
- package/src/plugins/builtin/notes/export.ts +114 -0
- package/src/plugins/builtin/notes/files.ts +315 -0
- package/src/plugins/builtin/notes/index.tsx +134 -0
- package/src/plugins/builtin/notes/markdown-note-preview.tsx +39 -0
- package/src/plugins/builtin/notes/migration.ts +94 -0
- package/src/plugins/builtin/notes/model.ts +36 -0
- package/src/plugins/builtin/notes/owner.tsx +131 -0
- package/src/plugins/builtin/notes/quick-notes-pane.tsx +503 -0
- package/src/plugins/builtin/notes/store.ts +314 -0
- package/src/plugins/builtin/notes/text-state.ts +11 -0
- package/src/plugins/builtin/notes/ticker-notes-tab.tsx +222 -0
- package/src/plugins/builtin/openfda/client.ts +420 -0
- package/src/plugins/builtin/openfda/index.tsx +73 -0
- package/src/plugins/builtin/openfda/model.ts +172 -0
- package/src/plugins/builtin/openfda/pane.tsx +462 -0
- package/src/plugins/builtin/openfda/types.ts +101 -0
- package/src/plugins/builtin/options/analytics-as-of.tsx +41 -0
- package/src/plugins/builtin/options/analytics.ts +258 -0
- package/src/plugins/builtin/options/calc-seed.ts +65 -0
- package/src/plugins/builtin/options/carry.ts +131 -0
- package/src/plugins/builtin/options/enrichment-client.ts +70 -0
- package/src/plugins/builtin/options/enrichment-model.ts +168 -0
- package/src/plugins/builtin/options/enrichment.ts +139 -0
- package/src/plugins/builtin/options/expiry-catalogue.ts +43 -0
- package/src/plugins/builtin/options/footer.ts +143 -0
- package/src/plugins/builtin/options/index.tsx +159 -0
- package/src/plugins/builtin/options/live-quotes.ts +295 -0
- package/src/plugins/builtin/options/market-reference.ts +92 -0
- package/src/plugins/builtin/options/model-spot.ts +163 -0
- package/src/plugins/builtin/options/open-interest-as-of.ts +74 -0
- package/src/plugins/builtin/options/quote-context.tsx +29 -0
- package/src/plugins/builtin/options/strike-window.ts +184 -0
- package/src/plugins/builtin/options/table.ts +365 -0
- package/src/plugins/builtin/options/types.ts +59 -0
- package/src/plugins/builtin/options/view.tsx +1055 -0
- package/src/plugins/builtin/options-calculator/binomial.ts +332 -0
- package/src/plugins/builtin/options-calculator/evidence.ts +228 -0
- package/src/plugins/builtin/options-calculator/headless.ts +137 -0
- package/src/plugins/builtin/options-calculator/index.tsx +37 -0
- package/src/plugins/builtin/options-calculator/inputs.ts +79 -0
- package/src/plugins/builtin/options-calculator/model.ts +119 -0
- package/src/plugins/builtin/options-calculator/pane.tsx +429 -0
- package/src/plugins/builtin/options-calculator/premium-history.ts +34 -0
- package/src/plugins/builtin/options-calculator/surface.ts +194 -0
- package/src/plugins/builtin/options-positioning/client.ts +149 -0
- package/src/plugins/builtin/options-positioning/headless.ts +255 -0
- package/src/plugins/builtin/options-positioning/index.tsx +72 -0
- package/src/plugins/builtin/options-positioning/model.ts +337 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +669 -0
- package/src/plugins/builtin/options-scenario/charts.tsx +47 -0
- package/src/plugins/builtin/options-scenario/client.ts +645 -0
- package/src/plugins/builtin/options-scenario/collar.ts +143 -0
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +259 -0
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +316 -0
- package/src/plugins/builtin/options-scenario/csp-screen.ts +298 -0
- package/src/plugins/builtin/options-scenario/csp.ts +254 -0
- package/src/plugins/builtin/options-scenario/editor.tsx +139 -0
- package/src/plugins/builtin/options-scenario/evidence.ts +127 -0
- package/src/plugins/builtin/options-scenario/freshness.ts +27 -0
- package/src/plugins/builtin/options-scenario/headless.ts +208 -0
- package/src/plugins/builtin/options-scenario/hedge.ts +291 -0
- package/src/plugins/builtin/options-scenario/index.tsx +64 -0
- package/src/plugins/builtin/options-scenario/live.ts +50 -0
- package/src/plugins/builtin/options-scenario/model.ts +407 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +475 -0
- package/src/plugins/builtin/options-scenario/state.ts +71 -0
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/client.ts +133 -0
- package/src/plugins/builtin/pe-band/headless.ts +69 -0
- package/src/plugins/builtin/pe-band/index.tsx +20 -0
- package/src/plugins/builtin/pe-band/model.ts +297 -0
- package/src/plugins/builtin/pe-band/pane.tsx +177 -0
- package/src/plugins/builtin/pe-band/report-dates.ts +82 -0
- 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 +326 -0
- package/src/plugins/builtin/perps/equity-identity.ts +16 -0
- package/src/plugins/builtin/perps/equity-row.tsx +76 -0
- package/src/plugins/builtin/perps/freshness.ts +71 -0
- package/src/plugins/builtin/perps/headless.ts +263 -0
- package/src/plugins/builtin/perps/index.tsx +35 -0
- package/src/plugins/builtin/perps/list-perpetuals.ts +139 -0
- package/src/plugins/builtin/perps/live-rows.ts +168 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/model.ts +349 -0
- package/src/plugins/builtin/perps/open-market.ts +15 -0
- package/src/plugins/builtin/perps/pane.tsx +463 -0
- package/src/plugins/builtin/perps/series.ts +76 -0
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/perps/venue-markets.ts +96 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +44 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +1059 -0
- package/src/plugins/builtin/plugin-module.ts +209 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +206 -0
- package/src/plugins/builtin/portfolio-list/cached-account.ts +44 -0
- package/src/plugins/builtin/portfolio-list/cell-version.ts +79 -0
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +324 -0
- package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +1151 -0
- package/src/plugins/builtin/portfolio-list/cli/render.ts +1404 -0
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +151 -0
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +297 -0
- package/src/plugins/builtin/portfolio-list/grid.tsx +288 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +65 -0
- package/src/plugins/builtin/portfolio-list/headless.ts +552 -0
- package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
- package/src/plugins/builtin/portfolio-list/income.ts +307 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +125 -0
- package/src/plugins/builtin/portfolio-list/metrics.ts +25 -0
- package/src/plugins/builtin/portfolio-list/pane/data.ts +277 -0
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +846 -0
- package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
- package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +85 -0
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +331 -0
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +301 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +435 -0
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +210 -0
- package/src/plugins/builtin/portfolio-list/row-actions.ts +8 -0
- package/src/plugins/builtin/portfolio-list/settings.ts +409 -0
- package/src/plugins/builtin/portfolio-list/since-last-seen.ts +197 -0
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +530 -0
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +167 -0
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +280 -0
- package/src/plugins/builtin/portfolio-list/table.tsx +251 -0
- package/src/plugins/builtin/portfolio-list/use-column-clock.ts +41 -0
- package/src/plugins/builtin/portfolio-list/use-cursor-neighbor-prefetch.ts +87 -0
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +26 -0
- package/src/plugins/builtin/portfolio-list/use-throttled-memo.ts +59 -0
- package/src/plugins/builtin/portfolio-list/use-throttled-ticker-order.ts +78 -0
- package/src/plugins/builtin/power/client.ts +119 -0
- package/src/plugins/builtin/power/detail.tsx +82 -0
- package/src/plugins/builtin/power/evidence.ts +153 -0
- package/src/plugins/builtin/power/gas-client.ts +110 -0
- package/src/plugins/builtin/power/gas-model.ts +194 -0
- package/src/plugins/builtin/power/gas.tsx +149 -0
- package/src/plugins/builtin/power/generation.tsx +37 -0
- package/src/plugins/builtin/power/headless.ts +116 -0
- package/src/plugins/builtin/power/index.tsx +51 -0
- package/src/plugins/builtin/power/model.ts +160 -0
- package/src/plugins/builtin/power/pane.tsx +248 -0
- package/src/plugins/builtin/power/prices-client.ts +139 -0
- package/src/plugins/builtin/power/prices-headless.ts +206 -0
- package/src/plugins/builtin/power/prices-model.ts +103 -0
- package/src/plugins/builtin/power/prices.tsx +209 -0
- package/src/plugins/builtin/power/query.ts +25 -0
- package/src/plugins/builtin/primary-dealers/client.ts +158 -0
- package/src/plugins/builtin/primary-dealers/headless.ts +78 -0
- package/src/plugins/builtin/primary-dealers/index.tsx +18 -0
- package/src/plugins/builtin/primary-dealers/model.ts +256 -0
- package/src/plugins/builtin/primary-dealers/pane.tsx +269 -0
- package/src/plugins/builtin/rate-path/client.ts +52 -0
- package/src/plugins/builtin/rate-path/headless.ts +93 -0
- package/src/plugins/builtin/rate-path/index.tsx +18 -0
- package/src/plugins/builtin/rate-path/model.ts +127 -0
- package/src/plugins/builtin/rate-path/pane.tsx +214 -0
- package/src/plugins/builtin/realized-vol/chart-model.ts +70 -0
- package/src/plugins/builtin/realized-vol/charts.tsx +44 -0
- package/src/plugins/builtin/realized-vol/client.ts +108 -0
- package/src/plugins/builtin/realized-vol/evidence.ts +103 -0
- package/src/plugins/builtin/realized-vol/headless.ts +86 -0
- package/src/plugins/builtin/realized-vol/index.tsx +34 -0
- package/src/plugins/builtin/realized-vol/model.ts +149 -0
- package/src/plugins/builtin/realized-vol/pane.tsx +211 -0
- package/src/plugins/builtin/realized-vol/settings.ts +16 -0
- package/src/plugins/builtin/relative-rotation/client.ts +204 -0
- package/src/plugins/builtin/relative-rotation/headless.ts +95 -0
- package/src/plugins/builtin/relative-rotation/index.tsx +92 -0
- package/src/plugins/builtin/relative-rotation/model.ts +301 -0
- package/src/plugins/builtin/relative-rotation/pane.tsx +386 -0
- package/src/plugins/builtin/research/analyst-headless.ts +200 -0
- package/src/plugins/builtin/research/analyst-model.ts +557 -0
- package/src/plugins/builtin/research/analyst-pane.tsx +319 -0
- package/src/plugins/builtin/research/client.ts +12 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +716 -0
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +597 -0
- package/src/plugins/builtin/research/event-model.ts +525 -0
- package/src/plugins/builtin/research/event-sources.ts +72 -0
- package/src/plugins/builtin/research/events-headless.ts +82 -0
- package/src/plugins/builtin/research/headless-schema.ts +48 -0
- package/src/plugins/builtin/research/headless.ts +138 -0
- package/src/plugins/builtin/research/index.tsx +183 -0
- package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +340 -0
- package/src/plugins/builtin/research/relative-valuation-currency.ts +171 -0
- package/src/plugins/builtin/research/relative-valuation-headless.ts +239 -0
- package/src/plugins/builtin/research/relative-valuation-model.ts +240 -0
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +369 -0
- package/src/plugins/builtin/research/relative-valuation-peers.ts +302 -0
- package/src/plugins/builtin/research-search/article.ts +65 -0
- package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
- package/src/plugins/builtin/research-search/data.ts +116 -0
- package/src/plugins/builtin/research-search/document-view.tsx +74 -0
- package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
- package/src/plugins/builtin/research-search/index.tsx +87 -0
- package/src/plugins/builtin/research-search/model.ts +266 -0
- package/src/plugins/builtin/research-search/pane.tsx +690 -0
- package/src/plugins/builtin/research-search/saved-view.tsx +172 -0
- package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
- package/src/plugins/builtin/research-search/snippet.ts +105 -0
- package/src/plugins/builtin/revenue-breakdown/bars.tsx +113 -0
- package/src/plugins/builtin/revenue-breakdown/client.ts +121 -0
- package/src/plugins/builtin/revenue-breakdown/headless.ts +89 -0
- package/src/plugins/builtin/revenue-breakdown/index.tsx +60 -0
- package/src/plugins/builtin/revenue-breakdown/model.ts +157 -0
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +293 -0
- package/src/plugins/builtin/reverse-dcf/client.ts +36 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +71 -0
- package/src/plugins/builtin/reverse-dcf/index.tsx +23 -0
- package/src/plugins/builtin/reverse-dcf/model.ts +229 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +133 -0
- package/src/plugins/builtin/risk-factors/data.ts +135 -0
- package/src/plugins/builtin/risk-factors/headless.ts +82 -0
- package/src/plugins/builtin/risk-factors/index.tsx +93 -0
- package/src/plugins/builtin/risk-factors/pane.tsx +308 -0
- package/src/plugins/builtin/scanner/denied.tsx +20 -0
- package/src/plugins/builtin/scanner/feed.ts +118 -0
- package/src/plugins/builtin/scanner/flow-history.ts +98 -0
- package/src/plugins/builtin/scanner/flow-model.ts +318 -0
- package/src/plugins/builtin/scanner/flow-pane.tsx +562 -0
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +418 -0
- package/src/plugins/builtin/scanner/hilo-bars.tsx +65 -0
- package/src/plugins/builtin/scanner/hilo-model.ts +96 -0
- package/src/plugins/builtin/scanner/hilo-pane.tsx +208 -0
- package/src/plugins/builtin/scanner/index.ts +153 -0
- 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 +19 -0
- package/src/plugins/builtin/seasonality/client.ts +76 -0
- package/src/plugins/builtin/seasonality/headless.ts +73 -0
- package/src/plugins/builtin/seasonality/index.tsx +20 -0
- package/src/plugins/builtin/seasonality/model.ts +243 -0
- package/src/plugins/builtin/seasonality/pane.tsx +229 -0
- package/src/plugins/builtin/sec/client.ts +16 -0
- package/src/plugins/builtin/sec/command-bar-search.ts +227 -0
- package/src/plugins/builtin/sec/filing-content.ts +160 -0
- package/src/plugins/builtin/sec/filing-display.tsx +53 -0
- package/src/plugins/builtin/sec/filing-documents.ts +65 -0
- package/src/plugins/builtin/sec/forms.ts +75 -0
- package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
- package/src/plugins/builtin/sec/fund-facts-view.tsx +56 -0
- package/src/plugins/builtin/sec/fund-facts.ts +470 -0
- package/src/plugins/builtin/sec/headless.ts +295 -0
- package/src/plugins/builtin/sec/index.tsx +690 -0
- package/src/plugins/builtin/sec/model.ts +266 -0
- package/src/plugins/builtin/sec/row-summaries.ts +105 -0
- package/src/plugins/builtin/sectors/client.ts +182 -0
- package/src/plugins/builtin/sectors/headless.ts +137 -0
- package/src/plugins/builtin/sectors/index.tsx +454 -0
- package/src/plugins/builtin/sectors/move-bar.tsx +21 -0
- package/src/plugins/builtin/sectors/sector-data.ts +75 -0
- package/src/plugins/builtin/sectors/sector-model.ts +536 -0
- package/src/plugins/builtin/series-inventory.ts +101 -0
- package/src/plugins/builtin/shared/broker-reconnect.ts +190 -0
- package/src/plugins/builtin/shared/cloud-denied.ts +47 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +70 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +367 -0
- package/src/plugins/builtin/shared/command-tab.ts +69 -0
- package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
- package/src/plugins/builtin/shared/earnings-calendar.ts +379 -0
- package/src/plugins/builtin/shared/earnings-estimate-basis.ts +71 -0
- package/src/plugins/builtin/shared/earnings-report.ts +108 -0
- package/src/plugins/builtin/shared/filing-year-reader.tsx +123 -0
- package/src/plugins/builtin/shared/fred-points.ts +20 -0
- package/src/plugins/builtin/shared/free-preview.ts +47 -0
- package/src/plugins/builtin/shared/free-routes.ts +24 -0
- package/src/plugins/builtin/shared/futures-session.ts +34 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +123 -0
- package/src/plugins/builtin/shared/headless-market-data.ts +72 -0
- package/src/plugins/builtin/shared/headless-options.ts +54 -0
- package/src/plugins/builtin/shared/history-span.ts +55 -0
- package/src/plugins/builtin/shared/job-posting.ts +130 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +58 -0
- package/src/plugins/builtin/shared/macro-releases.ts +65 -0
- package/src/plugins/builtin/shared/members-menu.tsx +261 -0
- package/src/plugins/builtin/shared/mine-tickers.ts +19 -0
- package/src/plugins/builtin/shared/move-rank.ts +108 -0
- package/src/plugins/builtin/shared/open-description.ts +29 -0
- package/src/plugins/builtin/shared/pane-request.ts +35 -0
- package/src/plugins/builtin/shared/pro-feature-wall.tsx +56 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +140 -0
- package/src/plugins/builtin/shared/public-pane.ts +9 -0
- package/src/plugins/builtin/shared/report-freshness.ts +149 -0
- package/src/plugins/builtin/shared/research-cells.tsx +102 -0
- package/src/plugins/builtin/shared/research-cloud-session.ts +18 -0
- package/src/plugins/builtin/shared/research-trading.tsx +85 -0
- package/src/plugins/builtin/shared/scanner-capture.ts +98 -0
- package/src/plugins/builtin/shared/series-cache.ts +140 -0
- package/src/plugins/builtin/shared/series-detail-chart.tsx +171 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +223 -0
- package/src/plugins/builtin/shared/series-warning.ts +5 -0
- package/src/plugins/builtin/shared/stat-transform.ts +94 -0
- package/src/plugins/builtin/shared/ticker-choice.tsx +35 -0
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/ticker-request.ts +71 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +149 -0
- package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
- package/src/plugins/builtin/shared/trend.ts +81 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +695 -0
- package/src/plugins/builtin/shared/volatility/index.ts +4 -0
- package/src/plugins/builtin/shared/volatility/live-session.ts +211 -0
- package/src/plugins/builtin/shared/volatility/options.ts +212 -0
- package/src/plugins/builtin/shared/volatility/pricing.ts +236 -0
- package/src/plugins/builtin/shared/volatility/smile.ts +413 -0
- package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
- package/src/plugins/builtin/shared/wall-teaser-catalog.ts +80 -0
- package/src/plugins/builtin/shared/wall-teaser.tsx +192 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +45 -0
- package/src/plugins/builtin/short-interest/client.ts +48 -0
- package/src/plugins/builtin/short-interest/headless.ts +166 -0
- package/src/plugins/builtin/short-interest/index.tsx +143 -0
- package/src/plugins/builtin/short-interest/model.ts +265 -0
- package/src/plugins/builtin/short-interest/pane.tsx +178 -0
- package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
- package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
- package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
- package/src/plugins/builtin/short-interest/schedule.ts +140 -0
- package/src/plugins/builtin/short-interest/series.ts +36 -0
- package/src/plugins/builtin/short-interest/surface.tsx +49 -0
- package/src/plugins/builtin/short-interest/types.ts +16 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +122 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +56 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +214 -0
- package/src/plugins/builtin/short-interest/watch-pane.tsx +173 -0
- package/src/plugins/builtin/short-volume/client.ts +86 -0
- package/src/plugins/builtin/short-volume/headless.ts +31 -0
- package/src/plugins/builtin/short-volume/index.tsx +20 -0
- package/src/plugins/builtin/short-volume/model.ts +53 -0
- package/src/plugins/builtin/short-volume/pane.tsx +108 -0
- package/src/plugins/builtin/short-volume/series.ts +23 -0
- package/src/plugins/builtin/social-mentions/client.ts +93 -0
- package/src/plugins/builtin/social-mentions/headless.ts +33 -0
- package/src/plugins/builtin/social-mentions/index.tsx +24 -0
- package/src/plugins/builtin/social-mentions/lock-note.ts +15 -0
- package/src/plugins/builtin/social-mentions/model.ts +92 -0
- package/src/plugins/builtin/social-mentions/pane.tsx +158 -0
- package/src/plugins/builtin/social-mentions/series.ts +23 -0
- package/src/plugins/builtin/supply-chain/client.ts +113 -0
- package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +21 -0
- package/src/plugins/builtin/supply-chain/evidence.ts +90 -0
- package/src/plugins/builtin/supply-chain/flow-layout.ts +134 -0
- package/src/plugins/builtin/supply-chain/flow.tsx +366 -0
- package/src/plugins/builtin/supply-chain/graph-client.ts +86 -0
- package/src/plugins/builtin/supply-chain/graph-evidence.ts +25 -0
- package/src/plugins/builtin/supply-chain/graph-model.ts +53 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +221 -0
- package/src/plugins/builtin/supply-chain/graph.tsx +167 -0
- package/src/plugins/builtin/supply-chain/headless.ts +185 -0
- package/src/plugins/builtin/supply-chain/index.tsx +26 -0
- package/src/plugins/builtin/supply-chain/model.ts +292 -0
- package/src/plugins/builtin/supply-chain/pane.tsx +373 -0
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +97 -0
- package/src/plugins/builtin/supply-chain/trust.ts +34 -0
- package/src/plugins/builtin/themes/client.ts +39 -0
- package/src/plugins/builtin/themes/headless.ts +129 -0
- package/src/plugins/builtin/themes/model.ts +208 -0
- package/src/plugins/builtin/themes/pane.tsx +166 -0
- package/src/plugins/builtin/thirteenf/api.ts +434 -0
- package/src/plugins/builtin/thirteenf/data.ts +593 -0
- package/src/plugins/builtin/thirteenf/format.ts +56 -0
- package/src/plugins/builtin/thirteenf/headless.ts +927 -0
- package/src/plugins/builtin/thirteenf/index.tsx +94 -0
- package/src/plugins/builtin/thirteenf/model.ts +933 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +207 -0
- package/src/plugins/builtin/thirteenf/overlap.ts +123 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +781 -0
- package/src/plugins/builtin/thirteenf/report-notes.ts +329 -0
- package/src/plugins/builtin/thirteenf/signals-pane.tsx +153 -0
- package/src/plugins/builtin/thirteenf/signals.ts +74 -0
- package/src/plugins/builtin/thirteenf/table.ts +196 -0
- package/src/plugins/builtin/thirteenf/types.ts +254 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +419 -0
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +112 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +179 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +425 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +361 -0
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +322 -0
- package/src/plugins/builtin/ticker-detail/financials/model.ts +643 -0
- package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
- package/src/plugins/builtin/ticker-detail/financials/pane.tsx +29 -0
- package/src/plugins/builtin/ticker-detail/financials/period-end-history.ts +44 -0
- package/src/plugins/builtin/ticker-detail/financials/ratio-table.tsx +175 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +830 -0
- package/src/plugins/builtin/ticker-detail/financials/schema.ts +389 -0
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +592 -0
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +111 -0
- package/src/plugins/builtin/ticker-detail/headless.ts +591 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +248 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +361 -0
- package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
- package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +446 -0
- package/src/plugins/builtin/ticker-detail/overview/option-contract.ts +64 -0
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +60 -0
- package/src/plugins/builtin/ticker-detail/overview/types.ts +40 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +618 -0
- package/src/plugins/builtin/ticker-detail/pane.tsx +420 -0
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +48 -0
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +489 -0
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +320 -0
- package/src/plugins/builtin/ticker-detail/research-tab-keys.tsx +58 -0
- package/src/plugins/builtin/ticker-detail/research-tab-navigation.tsx +14 -0
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +43 -0
- package/src/plugins/builtin/ticker-detail/settings.ts +204 -0
- package/src/plugins/builtin/time-sales/client.ts +84 -0
- package/src/plugins/builtin/time-sales/headless.ts +117 -0
- package/src/plugins/builtin/time-sales/index.tsx +18 -0
- package/src/plugins/builtin/time-sales/model.ts +125 -0
- package/src/plugins/builtin/time-sales/pane.tsx +189 -0
- package/src/plugins/builtin/time-sales/snapshot-client.ts +28 -0
- package/src/plugins/builtin/time-sales/use-tape.ts +58 -0
- package/src/plugins/builtin/time-sales/window.ts +128 -0
- package/src/plugins/builtin/trace-bonds/client.ts +525 -0
- package/src/plugins/builtin/trace-bonds/headless.ts +107 -0
- package/src/plugins/builtin/trace-bonds/index.tsx +35 -0
- package/src/plugins/builtin/trace-bonds/model.ts +117 -0
- package/src/plugins/builtin/trace-bonds/pane.tsx +209 -0
- package/src/plugins/builtin/treasury-auctions/cache.ts +115 -0
- package/src/plugins/builtin/treasury-auctions/client.ts +230 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +172 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +88 -0
- package/src/plugins/builtin/treasury-auctions/model.ts +379 -0
- package/src/plugins/builtin/treasury-auctions/pane.tsx +413 -0
- package/src/plugins/builtin/treasury-auctions/types.ts +72 -0
- package/src/plugins/builtin/treasury-daily/client.ts +132 -0
- package/src/plugins/builtin/treasury-daily/headless.ts +69 -0
- package/src/plugins/builtin/treasury-daily/index.tsx +17 -0
- package/src/plugins/builtin/treasury-daily/model.ts +116 -0
- package/src/plugins/builtin/treasury-daily/pane.tsx +219 -0
- package/src/plugins/builtin/vol-surface/charts.tsx +117 -0
- package/src/plugins/builtin/vol-surface/client.ts +260 -0
- package/src/plugins/builtin/vol-surface/evidence.ts +207 -0
- package/src/plugins/builtin/vol-surface/headless.ts +286 -0
- package/src/plugins/builtin/vol-surface/heat-grid.tsx +113 -0
- package/src/plugins/builtin/vol-surface/index.tsx +47 -0
- package/src/plugins/builtin/vol-surface/live.ts +60 -0
- package/src/plugins/builtin/vol-surface/model.ts +661 -0
- package/src/plugins/builtin/vol-surface/pane.tsx +582 -0
- package/src/plugins/builtin/vol-surface/raster.ts +113 -0
- package/src/plugins/builtin/vol-surface/stored.ts +170 -0
- package/src/plugins/builtin/vol-surface/surface.tsx +59 -0
- package/src/plugins/builtin/vol-surface/tables.ts +166 -0
- package/src/plugins/builtin/volatility/chart-model.ts +163 -0
- package/src/plugins/builtin/volatility/charts.tsx +40 -0
- package/src/plugins/builtin/volatility/client.ts +256 -0
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/evidence.ts +144 -0
- package/src/plugins/builtin/volatility/headless.ts +78 -0
- package/src/plugins/builtin/volatility/index.tsx +27 -0
- package/src/plugins/builtin/volatility/model.ts +443 -0
- package/src/plugins/builtin/volatility/pane.tsx +254 -0
- package/src/plugins/builtin/volatility/series.ts +119 -0
- package/src/plugins/builtin/world-indices/client.ts +58 -0
- package/src/plugins/builtin/world-indices/footer.ts +25 -0
- package/src/plugins/builtin/world-indices/headless.ts +148 -0
- package/src/plugins/builtin/world-indices/index.tsx +189 -0
- package/src/plugins/builtin/world-indices/indices.ts +63 -0
- package/src/plugins/builtin/world-indices/model.ts +94 -0
- package/src/plugins/builtin/world-indices/table.tsx +105 -0
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +161 -0
- package/src/plugins/builtin/world-venue-map/basemap.ts +237 -0
- package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
- package/src/plugins/builtin/world-venue-map/client.ts +45 -0
- package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
- package/src/plugins/builtin/world-venue-map/entity-detail.tsx +160 -0
- package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
- package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
- package/src/plugins/builtin/world-venue-map/geo-draw.tsx +249 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +255 -0
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +448 -0
- package/src/plugins/builtin/world-venue-map/headless.ts +166 -0
- package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
- package/src/plugins/builtin/world-venue-map/index.ts +82 -0
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +576 -0
- package/src/plugins/builtin/world-venue-map/layers.ts +435 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +1122 -0
- package/src/plugins/builtin/world-venue-map/model.ts +321 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +202 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +260 -0
- package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +43 -0
- package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
- package/src/plugins/builtin/yield-curve/chart.ts +159 -0
- package/src/plugins/builtin/yield-curve/client.ts +25 -0
- package/src/plugins/builtin/yield-curve/curves.ts +225 -0
- package/src/plugins/builtin/yield-curve/forward.ts +102 -0
- package/src/plugins/builtin/yield-curve/headless.ts +287 -0
- package/src/plugins/builtin/yield-curve/history.ts +166 -0
- package/src/plugins/builtin/yield-curve/index.tsx +494 -0
- package/src/plugins/builtin/yield-curve/series.ts +96 -0
- package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
- package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
- package/src/plugins/builtin/yield-curve/treasury-data.ts +73 -0
- package/src/plugins/builtin/yield-curve/world.ts +57 -0
- package/src/plugins/builtin-editorial.ts +126 -0
- package/src/plugins/bundle.ts +200 -0
- package/src/plugins/catalog-backend.ts +37 -0
- package/src/plugins/catalog-browser.ts +344 -0
- package/src/plugins/catalog-manifest.ts +144 -0
- package/src/plugins/catalog-ui.ts +84 -0
- package/src/plugins/catalog.ts +44 -0
- package/src/plugins/compat.ts +225 -0
- package/src/plugins/current-target.ts +30 -0
- package/src/plugins/dependencies.ts +143 -0
- package/src/plugins/event-bus.ts +43 -0
- package/src/plugins/external-runtime.ts +54 -0
- package/src/plugins/extracted-plugins.ts +202 -0
- package/src/plugins/health.ts +21 -0
- package/src/plugins/host-link.ts +422 -0
- package/src/plugins/host-modules.ts +141 -0
- package/src/plugins/host-resolver.ts +100 -0
- package/src/plugins/host-smoke.ts +103 -0
- package/src/plugins/installer.ts +649 -0
- package/src/plugins/loader.ts +580 -0
- package/src/plugins/manager-node.ts +55 -0
- package/src/plugins/marketplace/activation.ts +95 -0
- package/src/plugins/marketplace/deactivation.ts +22 -0
- package/src/plugins/marketplace/feed.ts +92 -0
- package/src/plugins/marketplace/ids.ts +2 -0
- package/src/plugins/marketplace/model.ts +598 -0
- package/src/plugins/marketplace/packs.ts +118 -0
- package/src/plugins/marketplace/store.ts +130 -0
- package/src/plugins/ownership.ts +281 -0
- package/src/plugins/peer-retry.ts +155 -0
- package/src/plugins/plugin-export.ts +24 -0
- package/src/plugins/plugin-functions.ts +54 -0
- package/src/plugins/plugin-icon.ts +61 -0
- package/src/plugins/plugin-names.ts +32 -0
- package/src/plugins/plugin-off.ts +36 -0
- package/src/plugins/plugin-persistence.ts +84 -0
- package/src/plugins/registry/context-menu.ts +36 -0
- package/src/plugins/registry/contributions.ts +159 -0
- package/src/plugins/registry/host-actions.ts +276 -0
- package/src/plugins/registry/index.ts +777 -0
- package/src/plugins/registry/pane-settings.ts +188 -0
- package/src/plugins/registry/plugin-state.ts +135 -0
- package/src/plugins/registry/setup-command.ts +93 -0
- package/src/plugins/registry/shared.ts +28 -0
- package/src/plugins/registry/slots.ts +67 -0
- package/src/plugins/runtime/context.tsx +110 -0
- package/src/plugins/runtime/index.tsx +95 -0
- package/src/plugins/runtime/state.ts +256 -0
- package/src/plugins/seed-notice.ts +20 -0
- package/src/plugins/shortcut-argument.ts +39 -0
- package/src/plugins/team-state.ts +70 -0
- package/src/plugins/web-bundled.ts +33 -0
- package/src/portfolio/allocation.ts +316 -0
- package/src/portfolio/collection-name.ts +62 -0
- package/src/portfolio/column-values.ts +846 -0
- package/src/portfolio/cost-fx.ts +338 -0
- package/src/portfolio/futures-positions.ts +216 -0
- package/src/portfolio/futures-terms.ts +75 -0
- package/src/portfolio/manual-lots.ts +153 -0
- package/src/portfolio/mutations.ts +532 -0
- package/src/portfolio/position-metrics.ts +332 -0
- package/src/portfolio/position-quantity.ts +49 -0
- package/src/portfolio/position-value.ts +40 -0
- package/src/portfolio/suffixed-listings.ts +209 -0
- package/src/portfolio/totals-currency.ts +24 -0
- package/src/portfolio/valuation-basis.ts +89 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +557 -0
- package/src/public/broker.ts +50 -0
- package/src/public/i18n.ts +17 -0
- package/src/public/index.ts +3 -0
- package/src/public/layout.ts +12 -0
- package/src/public/market-data.ts +43 -0
- package/src/public/pane-hooks.ts +72 -0
- package/src/public/quotes.ts +50 -0
- package/src/public/react.ts +130 -0
- package/src/public/remote.ts +23 -0
- package/src/public/test-support.ts +84 -0
- package/src/public/tickers.ts +40 -0
- package/src/public/time-series.ts +61 -0
- package/src/public/utils.ts +112 -0
- package/src/react/assign-ref.ts +10 -0
- package/src/react/async-resource.ts +79 -0
- package/src/react/auto-refresh.ts +107 -0
- package/src/react/input.ts +278 -0
- package/src/react/key-repeat.ts +86 -0
- package/src/react/plugins/plugin-slot.tsx +16 -0
- package/src/react/read-state.ts +104 -0
- package/src/react/ticker-source.ts +32 -0
- package/src/react/use-raf-callback.ts +44 -0
- package/src/react/use-throttled-commit-value.ts +133 -0
- package/src/remote/app-host.tsx +115 -0
- package/src/remote/client.ts +34 -0
- package/src/remote/command-bar.ts +98 -0
- package/src/remote/confirm-change.tsx +86 -0
- package/src/remote/controller-utils.ts +113 -0
- package/src/remote/controller.ts +727 -0
- package/src/remote/endpoint.ts +41 -0
- package/src/remote/form.ts +20 -0
- package/src/remote/json-patch.ts +103 -0
- package/src/remote/layout-helpers.ts +55 -0
- package/src/remote/matching.ts +69 -0
- package/src/remote/pane-screenshot.ts +47 -0
- package/src/remote/resources.ts +236 -0
- package/src/remote/revision.ts +5 -0
- package/src/remote/schema.ts +544 -0
- package/src/remote/semantic-helpers.ts +161 -0
- package/src/remote/semantic-tree.tsx +167 -0
- package/src/remote/server.ts +131 -0
- package/src/remote/types.ts +211 -0
- package/src/remote/watchlist.ts +314 -0
- package/src/renderers/browser/app-services.ts +33 -0
- package/src/renderers/browser/bundled-plugins.ts +91 -0
- package/src/renderers/browser/cloud-transport.ts +111 -0
- package/src/renderers/browser/config-host.ts +146 -0
- package/src/renderers/browser/deeplink-bridge.ts +52 -0
- package/src/renderers/browser/http-proxy-transport.ts +55 -0
- package/src/renderers/browser/main.tsx +142 -0
- package/src/renderers/browser/research-entry.ts +23 -0
- package/src/renderers/browser/ui-host.tsx +51 -0
- package/src/renderers/cloudflare/http-proxy.ts +289 -0
- package/src/renderers/cloudflare/proxy-session.ts +270 -0
- package/src/renderers/cloudflare/worker.ts +145 -0
- package/src/renderers/dom/boot-frame-page.ts +187 -0
- package/src/renderers/dom/boot-frame.ts +237 -0
- package/src/renderers/dom/build-assets.ts +273 -0
- package/src/renderers/dom/data-frames.ts +24 -0
- package/src/renderers/dom/data-table/dom.ts +253 -0
- package/src/renderers/dom/data-table/frozen-column.ts +24 -0
- package/src/renderers/dom/data-table/index.tsx +558 -0
- package/src/renderers/dom/data-table/row.tsx +506 -0
- package/src/renderers/dom/desktop/audio-player.tsx +312 -0
- package/src/renderers/dom/desktop/control-styles.ts +90 -0
- package/src/renderers/dom/desktop/controls.tsx +706 -0
- package/src/renderers/dom/desktop/dialog-actions.tsx +130 -0
- package/src/renderers/dom/desktop/icons.tsx +258 -0
- package/src/renderers/dom/desktop/list-view.tsx +334 -0
- package/src/renderers/dom/desktop/menu.tsx +115 -0
- package/src/renderers/dom/desktop/popover.tsx +249 -0
- package/src/renderers/dom/desktop/query-bar.tsx +352 -0
- package/src/renderers/dom/desktop/select-field.tsx +155 -0
- package/src/renderers/dom/desktop/stack-header.ts +22 -0
- package/src/renderers/dom/dialog-host.tsx +148 -0
- package/src/renderers/dom/dom-host-providers.tsx +64 -0
- package/src/renderers/dom/dom-ui-host.tsx +121 -0
- package/src/renderers/dom/host/ascii-text.ts +51 -0
- package/src/renderers/dom/host/block-glyph-canvas.tsx +118 -0
- package/src/renderers/dom/host/box.tsx +368 -0
- package/src/renderers/dom/host/chart-surface.tsx +331 -0
- package/src/renderers/dom/host/drag-shield.ts +35 -0
- package/src/renderers/dom/host/focus-scope.ts +105 -0
- package/src/renderers/dom/host/hls.chunk.ts +3 -0
- package/src/renderers/dom/host/image-surface.tsx +49 -0
- package/src/renderers/dom/host/input.tsx +403 -0
- package/src/renderers/dom/host/live-frame.ts +66 -0
- package/src/renderers/dom/host/media-surface.tsx +270 -0
- package/src/renderers/dom/host/mouse.ts +183 -0
- package/src/renderers/dom/host/overflow-fade.ts +89 -0
- package/src/renderers/dom/host/resize-rows.ts +111 -0
- package/src/renderers/dom/host/scroll-box.tsx +344 -0
- package/src/renderers/dom/host/size-observer.ts +70 -0
- package/src/renderers/dom/host/sticky-scroll.ts +147 -0
- package/src/renderers/dom/host/style.ts +148 -0
- package/src/renderers/dom/host/surface-3d.tsx +707 -0
- package/src/renderers/dom/host/surface-fit.ts +413 -0
- package/src/renderers/dom/host/tabs.tsx +380 -0
- package/src/renderers/dom/host/text.tsx +174 -0
- package/src/renderers/dom/host/top-surface.ts +127 -0
- package/src/renderers/dom/input-host.tsx +163 -0
- package/src/renderers/dom/key-event.ts +186 -0
- package/src/renderers/dom/native-renderer.ts +114 -0
- package/src/renderers/dom/scrollbar-activity.ts +23 -0
- package/src/renderers/dom/styles.css +1633 -0
- package/src/renderers/dom/test-utils.tsx +79 -0
- package/src/renderers/dom/toast-host.tsx +95 -0
- package/src/renderers/dom/windowed-list.tsx +201 -0
- package/src/renderers/dom/windowed-rows.ts +65 -0
- package/src/renderers/electrobun/bun/application-menu/click.ts +66 -0
- package/src/renderers/electrobun/bun/application-menu/index.ts +137 -0
- package/src/renderers/electrobun/bun/context-menu/click.ts +42 -0
- package/src/renderers/electrobun/bun/context-menu/normalize.ts +62 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +241 -0
- package/src/renderers/electrobun/bun/desktop/app-badge.ts +37 -0
- package/src/renderers/electrobun/bun/desktop/app-icon.ts +127 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +119 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +603 -0
- package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +144 -0
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +275 -0
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +184 -0
- package/src/renderers/electrobun/bun/desktop/http-fetch.ts +84 -0
- package/src/renderers/electrobun/bun/desktop/http-stream.ts +191 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +198 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-badge.ts +38 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-icon.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/macos-objc.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +90 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +97 -0
- package/src/renderers/electrobun/bun/desktop/rpc-registry.ts +43 -0
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +119 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +722 -0
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +256 -0
- package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +74 -0
- package/src/renderers/electrobun/bun/external-plugins.ts +242 -0
- package/src/renderers/electrobun/bun/index.ts +185 -0
- package/src/renderers/electrobun/bun/menu-event.ts +17 -0
- package/src/renderers/electrobun/bun/tui-entry.ts +18 -0
- package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
- package/src/renderers/electrobun/bun/window/controls.ts +69 -0
- package/src/renderers/electrobun/bun/window/create.ts +87 -0
- package/src/renderers/electrobun/bun/window/events.ts +49 -0
- package/src/renderers/electrobun/bun/window/focus.ts +42 -0
- package/src/renderers/electrobun/bun/window/frame.ts +51 -0
- package/src/renderers/electrobun/bun/window/remembered-frame.ts +122 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +43 -0
- package/src/renderers/electrobun/bun/window/windows-icons.ts +55 -0
- package/src/renderers/electrobun/bun/window/windows-native.ts +96 -0
- package/src/renderers/electrobun/shared/app-name.ts +30 -0
- package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
- package/src/renderers/electrobun/shared/protocol.ts +372 -0
- package/src/renderers/electrobun/shared/quote-event-batch.ts +103 -0
- package/src/renderers/electrobun/shared/rpc-codec.ts +94 -0
- package/src/renderers/electrobun/view/app-badge-host.ts +17 -0
- package/src/renderers/electrobun/view/app-services.ts +38 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +235 -0
- package/src/renderers/electrobun/view/broker-remote-client.ts +157 -0
- package/src/renderers/electrobun/view/capability-stream-client.ts +31 -0
- package/src/renderers/electrobun/view/capability-subscription.ts +50 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +546 -0
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +179 -0
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +15 -0
- package/src/renderers/electrobun/view/config-host.ts +38 -0
- package/src/renderers/electrobun/view/context-menu.ts +147 -0
- package/src/renderers/electrobun/view/desktop/window/bridge.ts +68 -0
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +60 -0
- package/src/renderers/electrobun/view/external-plugins.ts +88 -0
- package/src/renderers/electrobun/view/fatal-screen.tsx +76 -0
- package/src/renderers/electrobun/view/host/native.ts +33 -0
- package/src/renderers/electrobun/view/host/window-move.ts +28 -0
- package/src/renderers/electrobun/view/http-fetch.ts +199 -0
- package/src/renderers/electrobun/view/main.tsx +309 -0
- package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +34 -0
- package/src/renderers/electrobun/view/news-source.ts +46 -0
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +156 -0
- package/src/renderers/electrobun/view/remote/backend-quote-subscription.ts +127 -0
- package/src/renderers/electrobun/view/remote/capability-invoker.ts +57 -0
- package/src/renderers/electrobun/view/remote/connection-health.ts +31 -0
- package/src/renderers/electrobun/view/remote/persistence.ts +149 -0
- package/src/renderers/electrobun/view/remote/quote-subscription-registry.ts +74 -0
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +33 -0
- package/src/renderers/electrobun/view/renderer-window-error.ts +18 -0
- package/src/renderers/electrobun/view/rpc-timeout.ts +70 -0
- package/src/renderers/electrobun/view/screenshot-watermark.ts +105 -0
- package/src/renderers/electrobun/view/ui-host.tsx +80 -0
- package/src/renderers/electrobun/view/update-host.ts +62 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/electrobun/view/window-fullscreen.ts +57 -0
- package/src/renderers/opentui/browser-open.ts +88 -0
- package/src/renderers/opentui/chart-surface.tsx +152 -0
- package/src/renderers/opentui/dialog-host.tsx +229 -0
- package/src/renderers/opentui/frame-capture.ts +33 -0
- package/src/renderers/opentui/host.tsx +280 -0
- package/src/renderers/opentui/image/loader.ts +116 -0
- package/src/renderers/opentui/image/surface.tsx +107 -0
- package/src/renderers/opentui/input-host.tsx +41 -0
- package/src/renderers/opentui/interaction-performance.ts +214 -0
- package/src/renderers/opentui/kitty-services.ts +15 -0
- package/src/renderers/opentui/native-smoke.ts +10 -0
- package/src/renderers/opentui/native-surface.ts +176 -0
- package/src/renderers/opentui/pane-image/glyphs-license.txt +107 -0
- package/src/renderers/opentui/pane-image/glyphs.json +1 -0
- package/src/renderers/opentui/pane-image/index.ts +108 -0
- package/src/renderers/opentui/pane-image/raster.ts +428 -0
- package/src/renderers/opentui/render-lib.ts +253 -0
- package/src/renderers/opentui/render-timing-smoke.ts +20 -0
- package/src/renderers/opentui/scrollbox-layout.ts +35 -0
- package/src/renderers/opentui/start.tsx +191 -0
- package/src/renderers/opentui/terminal-input-reset.ts +25 -0
- package/src/renderers/opentui/terminal-media.ts +234 -0
- package/src/renderers/opentui/test-utils.tsx +310 -0
- package/src/renderers/opentui/toast-host.tsx +129 -0
- package/src/renderers/opentui/ui-host.tsx +271 -0
- package/src/renderers/share/chart.ts +74 -0
- package/src/renderers/share/layout-view.tsx +133 -0
- package/src/renderers/share/main.tsx +110 -0
- package/src/renderers/share/styles.css +83 -0
- package/src/renderers/share/view.tsx +159 -0
- package/src/series/capability.ts +76 -0
- package/src/series/csv.ts +160 -0
- package/src/series/files.ts +113 -0
- package/src/series/registry.ts +225 -0
- package/src/series/resolve.ts +227 -0
- package/src/series/standalone.ts +77 -0
- package/src/series/types.ts +220 -0
- package/src/series/validate.ts +216 -0
- package/src/shares/api.ts +150 -0
- package/src/shares/bug-report-dialog.tsx +162 -0
- package/src/shares/bug-report-flow.tsx +81 -0
- package/src/shares/bug-report.ts +42 -0
- package/src/shares/capture-flow.ts +20 -0
- package/src/shares/capture-preview.tsx +129 -0
- package/src/shares/chat.ts +23 -0
- package/src/shares/flow.tsx +98 -0
- package/src/shares/location.ts +40 -0
- package/src/shares/open.ts +26 -0
- package/src/shares/pane-image.ts +166 -0
- package/src/shares/pane.ts +60 -0
- package/src/shares/payload.ts +206 -0
- package/src/shares/personal-pane.ts +86 -0
- package/src/shares/portable-layout.ts +734 -0
- package/src/shares/prepare.ts +20 -0
- package/src/shares/ready.ts +42 -0
- package/src/shares/test-harness.ts +62 -0
- package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +202 -0
- package/src/sources/gloomberb-cloud/index.ts +987 -0
- package/src/sources/gloomberb-cloud/news.ts +210 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +464 -0
- package/src/sources/gloomberb-cloud/perpetuals.ts +316 -0
- package/src/sources/history-coverage.ts +56 -0
- package/src/sources/history-omitted.ts +34 -0
- package/src/sources/history-result.ts +88 -0
- package/src/sources/history-retention.ts +177 -0
- package/src/sources/listing-symbols.ts +206 -0
- package/src/sources/listing-target.ts +45 -0
- package/src/sources/provider-errors.ts +170 -0
- package/src/sources/provider-router/batches.ts +233 -0
- package/src/sources/provider-router/brokers.ts +104 -0
- package/src/sources/provider-router/cache.ts +229 -0
- package/src/sources/provider-router/cached-routes.ts +261 -0
- package/src/sources/provider-router/connection-health.ts +48 -0
- package/src/sources/provider-router/financial-routes.ts +383 -0
- package/src/sources/provider-router/financials.ts +627 -0
- package/src/sources/provider-router/history.ts +973 -0
- package/src/sources/provider-router/index.ts +713 -0
- package/src/sources/provider-router/news.ts +51 -0
- package/src/sources/provider-router/primary.ts +228 -0
- package/src/sources/provider-router/quote-route.ts +70 -0
- package/src/sources/provider-router/realtime-access.ts +64 -0
- package/src/sources/provider-router/route-types.ts +34 -0
- package/src/sources/provider-router/routing.ts +44 -0
- package/src/sources/provider-router/search.ts +272 -0
- package/src/sources/provider-router/sources.ts +55 -0
- package/src/sources/provider-router/statement-history.ts +15 -0
- package/src/sources/provider-router/streaming.ts +359 -0
- package/src/sources/provider-router/supplemental.ts +53 -0
- package/src/sources/provider-router/test-support.ts +47 -0
- package/src/sources/sec-edgar/acceptance-time.ts +11 -0
- package/src/sources/sec-edgar/content.ts +207 -0
- package/src/sources/sec-edgar/fourth-quarter.ts +179 -0
- package/src/sources/sec-edgar/identity.ts +36 -0
- package/src/sources/sec-edgar.ts +890 -0
- package/src/sources/sec-registrant.ts +75 -0
- package/src/state/app/activity.ts +216 -0
- package/src/state/app/bootstrap.ts +418 -0
- package/src/state/app/context/index.tsx +656 -0
- package/src/state/app/input-capture.ts +12 -0
- package/src/state/app/tab-cursor.ts +222 -0
- package/src/state/config-save-scheduler.ts +38 -0
- package/src/state/hooks/inline-ticker-failures.ts +100 -0
- package/src/state/hooks/inline-tickers.ts +545 -0
- package/src/state/hooks/live-streaming.ts +60 -0
- package/src/state/hooks/live-ticker-financials.ts +171 -0
- package/src/state/hooks/pane-ticker.ts +58 -0
- package/src/state/hooks/quote-streaming.ts +326 -0
- package/src/state/hooks/session-snapshot.ts +101 -0
- package/src/state/pane-scroll-registry.tsx +299 -0
- package/src/state/pane-table-export-registry.ts +50 -0
- package/src/state/persist-scheduler.ts +123 -0
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/state/ticker-refresh-queue.ts +89 -0
- package/src/sync/auth-transition.ts +32 -0
- package/src/sync/baseline.ts +43 -0
- package/src/sync/controller.ts +575 -0
- package/src/sync/core-contributors.ts +1550 -0
- package/src/sync/local-only.ts +121 -0
- package/src/sync/profile-analytics.ts +42 -0
- package/src/sync/react.ts +89 -0
- package/src/sync/types.ts +142 -0
- package/src/team/collections-sync.ts +235 -0
- package/src/team/collections.ts +369 -0
- package/src/team/model.ts +210 -0
- package/src/team/pane-request.ts +30 -0
- package/src/team/store.ts +324 -0
- package/src/telemetry/attention-counts.ts +170 -0
- package/src/telemetry/benign-window-error.ts +7 -0
- package/src/telemetry/crash-reports-dom.ts +79 -0
- package/src/telemetry/crash-reports-node.ts +173 -0
- package/src/telemetry/crash-reports.ts +373 -0
- package/src/telemetry/live-config.ts +26 -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/update-failures-node.ts +59 -0
- package/src/telemetry/update-failures.ts +307 -0
- package/src/telemetry/usage-counts-dom.ts +22 -0
- package/src/telemetry/usage-counts.ts +393 -0
- package/src/test-support/app-store.ts +21 -0
- package/src/test-support/broker.ts +81 -0
- package/src/test-support/builtin-panes.ts +31 -0
- package/src/test-support/cli-context.ts +37 -0
- package/src/test-support/cloud-api.ts +85 -0
- package/src/test-support/config-store.ts +31 -0
- package/src/test-support/console.ts +40 -0
- package/src/test-support/daily-nav.ts +25 -0
- package/src/test-support/data-provider.ts +85 -0
- package/src/test-support/headless.ts +32 -0
- package/src/test-support/margin-account.ts +108 -0
- package/src/test-support/market-session.ts +12 -0
- package/src/test-support/moved-symbols.ts +64 -0
- package/src/test-support/network-guard.ts +35 -0
- package/src/test-support/news.ts +21 -0
- package/src/test-support/pane.tsx +72 -0
- package/src/test-support/plugin-persistence.ts +112 -0
- package/src/test-support/plugin-runtime.ts +145 -0
- package/src/test-support/preload.ts +14 -0
- package/src/test-support/quote-store.ts +49 -0
- package/src/test-support/renderer-host.ts +10 -0
- package/src/test-support/rpc-loopback.ts +26 -0
- package/src/test-support/shell-pane-keys.tsx +36 -0
- package/src/test-support/temp-db.ts +19 -0
- package/src/test-support/ticker.ts +15 -0
- package/src/test-support/time-series.ts +27 -0
- package/src/theme/color-utils.ts +126 -0
- package/src/theme/colors.ts +311 -0
- package/src/theme/font-scale.ts +71 -0
- package/src/theme/heat-colors.ts +455 -0
- package/src/theme/series-colors.ts +46 -0
- package/src/theme/theme-context.tsx +44 -0
- package/src/theme/themes.ts +822 -0
- package/src/theme/ui-tokens.ts +107 -0
- package/src/tickers/batched-lookups.ts +186 -0
- package/src/tickers/company-name-listing.ts +276 -0
- package/src/tickers/company-name.ts +231 -0
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-coverage.ts +112 -0
- package/src/tickers/instrument-kind.ts +165 -0
- package/src/tickers/instrument-label.ts +15 -0
- package/src/tickers/list.ts +36 -0
- package/src/tickers/listing-fate.ts +331 -0
- package/src/tickers/listing-name.ts +34 -0
- package/src/tickers/listing-venues.ts +208 -0
- package/src/tickers/metadata.ts +74 -0
- package/src/tickers/open-target.ts +183 -0
- package/src/tickers/perpetual-listing.ts +154 -0
- package/src/tickers/prediction-listing.ts +26 -0
- package/src/tickers/search/asset-classes.ts +106 -0
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/identity.ts +19 -0
- package/src/tickers/search/index.ts +977 -0
- package/src/tickers/search/ranking.ts +1002 -0
- package/src/tickers/search/result.ts +27 -0
- package/src/tickers/search/types.ts +63 -0
- package/src/tickers/search/upsert.ts +156 -0
- package/src/tickers/selection.ts +21 -0
- package/src/tickers/starter-symbols.ts +8 -0
- package/src/tickers/terminal-codes.ts +474 -0
- package/src/tickers/ticker-changes.ts +301 -0
- package/src/tickers/tokenizer.ts +37 -0
- package/src/tickers/us-listing.ts +35 -0
- package/src/time-series/alignment.ts +173 -0
- package/src/time-series/bar-aggregation.ts +243 -0
- package/src/time-series/bar-spacing.ts +19 -0
- package/src/time-series/base-series.ts +315 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-data.ts +329 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/date-window.ts +41 -0
- package/src/time-series/economic.ts +106 -0
- package/src/time-series/field-catalog.ts +318 -0
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/financial-period-coverage.ts +59 -0
- package/src/time-series/forward-valuation.ts +285 -0
- package/src/time-series/fundamentals.ts +1275 -0
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/history-quality.ts +68 -0
- package/src/time-series/history-window.ts +21 -0
- package/src/time-series/hooks.ts +88 -0
- package/src/time-series/ice-credit.ts +57 -0
- package/src/time-series/leverage.ts +100 -0
- package/src/time-series/live-bars.ts +428 -0
- package/src/time-series/live-quotes.ts +234 -0
- package/src/time-series/market-resolution.ts +55 -0
- package/src/time-series/market.ts +177 -0
- package/src/time-series/parsed-history-cache.ts +79 -0
- package/src/time-series/price-comparison.ts +346 -0
- package/src/time-series/price-units.ts +139 -0
- package/src/time-series/range.ts +29 -0
- package/src/time-series/reporting-currency.ts +37 -0
- package/src/time-series/reporting.ts +134 -0
- package/src/time-series/resolution.ts +554 -0
- package/src/time-series/resolve-context.ts +163 -0
- package/src/time-series/resolve.ts +1190 -0
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +242 -0
- package/src/time-series/session-bar.ts +149 -0
- package/src/time-series/spec.ts +558 -0
- package/src/time-series/studies.ts +1628 -0
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/trader-studies.ts +188 -0
- package/src/time-series/transforms.ts +173 -0
- package/src/time-series/types.ts +346 -0
- package/src/time-series/use-chart-resolution.ts +549 -0
- package/src/time-series/valuation-currency.ts +48 -0
- package/src/time-series/valuation-price.ts +81 -0
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/broker.ts +237 -0
- package/src/types/capability-route-source.ts +25 -0
- package/src/types/config.ts +1046 -0
- package/src/types/context-menu.ts +101 -0
- package/src/types/data-provider.ts +355 -0
- package/src/types/desktop-deeplink.ts +8 -0
- package/src/types/desktop-menu.ts +16 -0
- package/src/types/desktop-window.ts +39 -0
- package/src/types/exchange-rate.ts +19 -0
- package/src/types/financials.ts +1108 -0
- package/src/types/headless.ts +782 -0
- package/src/types/instrument.ts +90 -0
- package/src/types/issuer-listings.ts +28 -0
- package/src/types/news-source.ts +9 -0
- package/src/types/persistence.ts +22 -0
- package/src/types/plugin.ts +1160 -0
- package/src/types/price-history.ts +52 -0
- package/src/types/ticker-changes.ts +45 -0
- package/src/types/ticker.ts +126 -0
- package/src/types/trading.ts +253 -0
- package/src/ui/ascii-font.ts +97 -0
- package/src/ui/context-menu.tsx +481 -0
- package/src/ui/dialog-bridge.tsx +50 -0
- package/src/ui/dialog-context.ts +24 -0
- package/src/ui/dialog-stack.ts +126 -0
- package/src/ui/dialog.tsx +98 -0
- package/src/ui/host.tsx +830 -0
- package/src/ui/index.tsx +313 -0
- package/src/ui/live-resize.ts +91 -0
- package/src/ui/live-stretch.ts +153 -0
- package/src/ui/measured-cells.ts +64 -0
- package/src/ui/pointer-drag.ts +12 -0
- package/src/ui/qr.ts +84 -0
- package/src/ui/resize-settle.ts +128 -0
- package/src/ui/scroll-box-axis.ts +26 -0
- package/src/ui/sticky-scroll.ts +7 -0
- package/src/ui/toast.tsx +147 -0
- package/src/updater/channel.ts +51 -0
- package/src/updater/command-names.ts +180 -0
- package/src/updater/failure.ts +166 -0
- package/src/updater/github-releases.ts +142 -0
- package/src/updater/index.ts +677 -0
- package/src/utils/asset-category.ts +20 -0
- package/src/utils/async-deadline.ts +81 -0
- package/src/utils/back-navigation.ts +52 -0
- package/src/utils/base-currency.ts +108 -0
- package/src/utils/basis-points.ts +17 -0
- package/src/utils/broker-instances.ts +47 -0
- package/src/utils/calendar-date.ts +163 -0
- package/src/utils/cash-runway.ts +241 -0
- package/src/utils/central-banks.ts +118 -0
- package/src/utils/cli-output.ts +687 -0
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/contract-month.ts +16 -0
- package/src/utils/crypto-pair.ts +163 -0
- package/src/utils/csv.ts +54 -0
- package/src/utils/currency-names.ts +128 -0
- package/src/utils/currency-pair.ts +112 -0
- package/src/utils/currency-units.ts +255 -0
- package/src/utils/datetime-format.ts +174 -0
- package/src/utils/debug-log.ts +201 -0
- package/src/utils/decimal-comma.ts +238 -0
- package/src/utils/declared-dividends.ts +39 -0
- package/src/utils/depositary-receipt.ts +70 -0
- package/src/utils/display-number.ts +63 -0
- package/src/utils/dividend-cash.ts +48 -0
- package/src/utils/dom-screenshot-css.ts +265 -0
- package/src/utils/dom-screenshot-rasters.ts +278 -0
- package/src/utils/dom-screenshot.ts +860 -0
- package/src/utils/double-escape-close.ts +82 -0
- package/src/utils/errors.ts +4 -0
- package/src/utils/exchange-announcements.ts +71 -0
- package/src/utils/exchange-notation.generated.ts +360 -0
- package/src/utils/exchange-rate-snapshot.ts +82 -0
- package/src/utils/exchanges.ts +214 -0
- package/src/utils/external-url.ts +24 -0
- package/src/utils/financial-statements.ts +342 -0
- package/src/utils/fire-and-forget.ts +24 -0
- package/src/utils/fiscal-period.ts +47 -0
- package/src/utils/format.ts +446 -0
- package/src/utils/forward-dividend.ts +218 -0
- package/src/utils/fundamentals.ts +290 -0
- package/src/utils/futures-generic.ts +221 -0
- package/src/utils/fuzzy-search.ts +105 -0
- package/src/utils/fx-market-hours.ts +118 -0
- package/src/utils/fx-status.ts +47 -0
- package/src/utils/guards.ts +62 -0
- package/src/utils/hash.ts +23 -0
- package/src/utils/html-entities.ts +45 -0
- package/src/utils/http-proxy-response.ts +257 -0
- package/src/utils/http-transport.ts +31 -0
- package/src/utils/income-statement.ts +46 -0
- package/src/utils/inline-content-tokenizer.ts +116 -0
- package/src/utils/instrument-identity.ts +72 -0
- package/src/utils/keyboard.ts +31 -0
- package/src/utils/latest-financial-period.ts +34 -0
- package/src/utils/link-tokenizer.ts +93 -0
- package/src/utils/linux-libc.ts +54 -0
- package/src/utils/local-utc-offset.ts +39 -0
- package/src/utils/log-failure.ts +31 -0
- package/src/utils/main-thread-stall.ts +97 -0
- package/src/utils/markdown.ts +180 -0
- package/src/utils/market-capitalization.ts +207 -0
- package/src/utils/math.ts +12 -0
- package/src/utils/max-drawdown.ts +64 -0
- package/src/utils/network-failure.ts +86 -0
- package/src/utils/object-version.ts +17 -0
- package/src/utils/one-shot.ts +73 -0
- package/src/utils/option-expiry.ts +321 -0
- package/src/utils/options.ts +170 -0
- package/src/utils/perf-marks.ts +96 -0
- package/src/utils/platform.ts +34 -0
- package/src/utils/plugin-proxy-hosts.json +23 -0
- package/src/utils/plugin-proxy-hosts.ts +28 -0
- package/src/utils/price-earnings.ts +86 -0
- package/src/utils/price-history-integrity.ts +127 -0
- package/src/utils/price-history.ts +534 -0
- package/src/utils/price-window.ts +131 -0
- package/src/utils/release-date.ts +47 -0
- package/src/utils/reported-money.ts +35 -0
- package/src/utils/rpc-timeout-error.ts +31 -0
- package/src/utils/save-text-file.ts +36 -0
- package/src/utils/screenshot-watermark.ts +27 -0
- package/src/utils/search-focus-navigation.ts +24 -0
- package/src/utils/sec-eps-basis.ts +294 -0
- package/src/utils/sec-filings.ts +245 -0
- package/src/utils/sec.ts +258 -0
- package/src/utils/selection-clipboard.ts +99 -0
- package/src/utils/semver.ts +108 -0
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/shortcut-labels.ts +42 -0
- package/src/utils/slugify.ts +4 -0
- package/src/utils/sort-values.ts +78 -0
- package/src/utils/statement-gaps.ts +94 -0
- package/src/utils/text-wrap.ts +107 -0
- package/src/utils/throttled-fetch.ts +253 -0
- package/src/utils/timestamp.ts +54 -0
- package/src/utils/trailing-dividend.ts +179 -0
- package/src/utils/trailing-eps.ts +67 -0
- package/src/utils/typed-text.ts +21 -0
- package/src/utils/typo-distance.ts +22 -0
- package/src/utils/url.ts +12 -0
- package/src/utils/utc-time.ts +183 -0
- package/src/utils/zoned-date-time.ts +146 -0
- package/src/version.ts +9 -0
|
@@ -0,0 +1,3569 @@
|
|
|
1
|
+
import { logFailure } from "../../utils/log-failure";
|
|
2
|
+
import type { CliCommandDef } from "../../types/plugin";
|
|
3
|
+
import { TIME_RANGES, type TimeRange } from "../../time-series/range";
|
|
4
|
+
import { yearToDateStartIndex } from "../../market-data/market/year-to-date";
|
|
5
|
+
import type { EarningsEvent, QuoteBatchResult, QuoteSubscriptionTarget, SecFilingItem } from "../../types/data-provider";
|
|
6
|
+
import type { NewsArticle, NewsFeed, NewsQuery } from "../../news/types";
|
|
7
|
+
import type {
|
|
8
|
+
AnalystResearchData,
|
|
9
|
+
CorporateActionsData,
|
|
10
|
+
HolderData,
|
|
11
|
+
OptionsChain,
|
|
12
|
+
Quote,
|
|
13
|
+
TickerFinancials,
|
|
14
|
+
} from "../../types/financials";
|
|
15
|
+
import { currencyPriceMinDigits, formatMarketPrice, formatMarketPriceWithCurrency, penceFigure, quoteFormatOptions, resolveAssetDisplayKind, withCurrencyMinorDigits } from "../../market-data/market/format";
|
|
16
|
+
import { INDEX_POINTS_EXPORT_UNIT, isIndexLevel, reportedQuoteVolume } from "../../market-data/market/index-level";
|
|
17
|
+
import { changeBasisJson, changeBasisLabel, changeBasisNote, quoteChangeBasis, quotePreviousCloseDate, quoteSettleText } from "../../market-data/market/change-basis";
|
|
18
|
+
import {
|
|
19
|
+
EXTENDED_SESSION_LABELS,
|
|
20
|
+
getExtendedSessionDisplay,
|
|
21
|
+
getQuoteSessionFields,
|
|
22
|
+
getRegularSessionDisplay,
|
|
23
|
+
quoteHeadlineTime,
|
|
24
|
+
quoteSessionLabel,
|
|
25
|
+
quoteSessionStateLabel,
|
|
26
|
+
type ExtendedSession,
|
|
27
|
+
} from "../../market-data/market/status";
|
|
28
|
+
import { formatCompact, formatCurrency, formatDistributionAmount, formatNumber, formatPercent } from "../../utils/format";
|
|
29
|
+
import { withCliServices, withMarketData } from "../context";
|
|
30
|
+
import { isoDate, parsePositiveInt, requireOneArg, takeFlag, takeOption } from "./command-utils";
|
|
31
|
+
import type { BuiltinCliCommandDef } from "../command-options";
|
|
32
|
+
import { CLI_COMMAND_GROUPS, TABLE_SECTION_OPTION } from "../help";
|
|
33
|
+
import { CLI_EXIT_CODES } from "../exit-codes";
|
|
34
|
+
import {
|
|
35
|
+
formatChangePercentCell,
|
|
36
|
+
formatCountCell,
|
|
37
|
+
formatFractionPercentCell,
|
|
38
|
+
formatPriceRange,
|
|
39
|
+
quoteChangeText,
|
|
40
|
+
} from "../helpers";
|
|
41
|
+
import { cliArrow, cliStyles, colorBySign, renderSection, renderStats } from "../../utils/cli-output";
|
|
42
|
+
import { filledPeriodsNote, stalePeriodNote, statementHistoryNotes } from "../../utils/financial-statements";
|
|
43
|
+
import { formatPerShareNumber } from "../../utils/reported-money";
|
|
44
|
+
import { isCurrencyPairListing } from "../../utils/fx-market-hours";
|
|
45
|
+
import {
|
|
46
|
+
analystTargetCurrency,
|
|
47
|
+
formatAnalystPrice,
|
|
48
|
+
formatRatingLabel,
|
|
49
|
+
formatRecommendationMix,
|
|
50
|
+
recommendationTotal,
|
|
51
|
+
analystReferenceMetadata,
|
|
52
|
+
analystUpside,
|
|
53
|
+
formatAnalystReference,
|
|
54
|
+
} from "../../plugins/builtin/research/analyst-model";
|
|
55
|
+
import { getPublishedUsEquityCalendarYears, getPublishedUsEquitySession } from "../../market-data/published-us-sessions";
|
|
56
|
+
import { isQuoteStaleForCurrentSession, noPrintsTodayText } from "../../market-data/quotes/freshness";
|
|
57
|
+
import {
|
|
58
|
+
coinOptionsAlternativesText, isCoinWithoutOptions, isOptionsUnavailableError, listedOptionsAlternative, optionsUnavailableTitle,
|
|
59
|
+
} from "../../market-data/options-alternatives";
|
|
60
|
+
import {
|
|
61
|
+
atReportTtmRate, cliFuturesContract, futuresContractMetrics, exportedDividendFundamentals, fundamentalsReportTables, loadDividendDistributions,
|
|
62
|
+
loadReportFundFacts, renderFundamentalsReport, writesCurrencyCodes,
|
|
63
|
+
} from "./ticker";
|
|
64
|
+
import { createBaseConverter } from "../base-converter";
|
|
65
|
+
import { loadStatementFilings } from "../statement-filings";
|
|
66
|
+
import { loadBankFigures } from "../bank-section";
|
|
67
|
+
import { latestFinancialPeriod } from "../../utils/latest-financial-period";
|
|
68
|
+
import type { FuturesContractTermsPayload } from "../../api-client/futures-curve";
|
|
69
|
+
import { currencyCodeLabel } from "../../utils/currency-units";
|
|
70
|
+
import {
|
|
71
|
+
futuresContractLines, futuresPricePer, futuresRollLines, isFuturesInstrument, loadUsdPerMmbtu, usdPerMmbtuText, type UsdPerMmbtu,
|
|
72
|
+
} from "../../market-data/futures-contract";
|
|
73
|
+
import {
|
|
74
|
+
chainHasExpiry,
|
|
75
|
+
formatExpiryList,
|
|
76
|
+
loadOptionModelInputs,
|
|
77
|
+
loadOptionsChainOpenInterest,
|
|
78
|
+
chainCoinCurrency,
|
|
79
|
+
chainHasFilledIv,
|
|
80
|
+
formatCoinPremiumCell,
|
|
81
|
+
formatOptionDeltaCell,
|
|
82
|
+
formatOptionDollarCell,
|
|
83
|
+
formatOptionGammaCell,
|
|
84
|
+
formatOptionIvCell,
|
|
85
|
+
formatOptionQuoteCell,
|
|
86
|
+
isMarkedChain,
|
|
87
|
+
MARKED_OPTION_FIELDS,
|
|
88
|
+
OPTION_MODEL_RATE,
|
|
89
|
+
OPTION_VOLATILITY_FIELDS,
|
|
90
|
+
OPTIONS_USAGE,
|
|
91
|
+
optionRows,
|
|
92
|
+
optionsBasisFields,
|
|
93
|
+
optionsBasisFootnote,
|
|
94
|
+
parseOptionExpiration,
|
|
95
|
+
takeExpirationOption,
|
|
96
|
+
valueChainAtModelSpot,
|
|
97
|
+
type OptionsChainBasis,
|
|
98
|
+
} from "./options-chain";
|
|
99
|
+
import {
|
|
100
|
+
expirationHint, expiredExpiryText, expiryIsoDate, isLiveOptionExpiry, liveDefaultExpiry, liveOptionExpiries, missingExpiryMessage,
|
|
101
|
+
OPTION_EXPIRATION_FORMAT, OPTION_EXPIRATION_PLACEHOLDER, optionExpiryVenue, withLiveExpiryList,
|
|
102
|
+
} from "../../utils/option-expiry";
|
|
103
|
+
import { DATE_OPTION_EXAMPLES, isoDateOption, isoDateToday } from "../../utils/calendar-date";
|
|
104
|
+
import { apiClient } from "../../api-client";
|
|
105
|
+
import type { ExchangeAnnouncementsPayload } from "../../api-client/exchange-announcements";
|
|
106
|
+
import { exchangeAnnouncementsPage, listsExchangeAnnouncements, type ExchangeAnnouncementsPage } from "../../utils/exchange-announcements";
|
|
107
|
+
import { dailyFxLeg, fxCloseOnDate, type FxCloseReading } from "../../market-data/fx-closes";
|
|
108
|
+
import { declaredDividends } from "../../utils/declared-dividends";
|
|
109
|
+
import {
|
|
110
|
+
DEFAULT_LEAPS_CRITERIA, LEAPS_EXTRA_COLUMNS, LEAPS_USAGE, parseLeapsCriteria, runLeapsScreen, takeLeapsOptions,
|
|
111
|
+
} from "./options-leaps";
|
|
112
|
+
import type { CliResultColumn } from "../result";
|
|
113
|
+
import type { PriceHistoryResult } from "../../types/price-history";
|
|
114
|
+
import {
|
|
115
|
+
exportNumber,
|
|
116
|
+
exportRowsTable,
|
|
117
|
+
reportFooterLines,
|
|
118
|
+
selectReportTables,
|
|
119
|
+
type CliReportTables,
|
|
120
|
+
} from "../report-tables";
|
|
121
|
+
import { historyCoverageNotice } from "../../sources/history-coverage";
|
|
122
|
+
import { maxDrawdown, maxDrawdownText } from "../../utils/max-drawdown";
|
|
123
|
+
import { isPastPriceWindow, loadPriceWindowHistory, PRICE_WINDOW_FROM_HELP, PRICE_WINDOW_TO_HELP, priceWindowText, readPriceWindow, type PriceWindow } from "../../utils/price-window";
|
|
124
|
+
import {
|
|
125
|
+
BAR_INTERVAL_LABELS,
|
|
126
|
+
closedPeriodBarDay,
|
|
127
|
+
formingPeriodBar,
|
|
128
|
+
historyBarStatusNote,
|
|
129
|
+
venueTradingDay,
|
|
130
|
+
historyCoverageWarning,
|
|
131
|
+
historyFacts,
|
|
132
|
+
historyFlagNote,
|
|
133
|
+
historyGapNote,
|
|
134
|
+
historyJumpNote,
|
|
135
|
+
historyUnbrokenFrom,
|
|
136
|
+
historyAllRangeYears,
|
|
137
|
+
historyIntervalsByRange,
|
|
138
|
+
historyMissingVolumeNote,
|
|
139
|
+
historyVenueVolumeNote,
|
|
140
|
+
historyNotes,
|
|
141
|
+
historyReportsVolume,
|
|
142
|
+
historyPriceDecimals,
|
|
143
|
+
historyRows,
|
|
144
|
+
historyUnit,
|
|
145
|
+
quotedPriceDivisor,
|
|
146
|
+
historyVolume,
|
|
147
|
+
historyVolumeText,
|
|
148
|
+
type HistoryRow,
|
|
149
|
+
type HistoryUnit,
|
|
150
|
+
type HistoryVolume,
|
|
151
|
+
} from "../history-rows";
|
|
152
|
+
import {
|
|
153
|
+
HISTORY_EXPORT_COLUMNS,
|
|
154
|
+
historyExportStatus,
|
|
155
|
+
historyFailureRow,
|
|
156
|
+
oneLineFailure,
|
|
157
|
+
symbolListNotes,
|
|
158
|
+
type HistoryListRow,
|
|
159
|
+
} from "../history-export";
|
|
160
|
+
import { fail, isCliFailure, NO_DATA_CODE } from "../errors";
|
|
161
|
+
import { mapWithConcurrency } from "../../utils/concurrency";
|
|
162
|
+
import { MISSING_VOLUME_TEXT } from "../../utils/price-history-integrity";
|
|
163
|
+
import { CRYPTO_BOARD_HINT, quoteNotes } from "./crypto-hints";
|
|
164
|
+
import { bareFuturesCode, futuresBoardContract } from "../../market-data/futures-board";
|
|
165
|
+
import { commodityNamedIn, commoditySearchName } from "../../market-data/commodity-names";
|
|
166
|
+
import { newsEmptyMessage } from "./news-hints";
|
|
167
|
+
import {
|
|
168
|
+
commodityNewsRoute,
|
|
169
|
+
commonWordCandidate,
|
|
170
|
+
METALS_NEWS_TOPIC,
|
|
171
|
+
metalCodeNewsRoute,
|
|
172
|
+
type MetalNewsRoute,
|
|
173
|
+
metalNewsHeading,
|
|
174
|
+
metalNewsRoute,
|
|
175
|
+
newsFallbackText,
|
|
176
|
+
newsSearchHeading,
|
|
177
|
+
newsSearchText,
|
|
178
|
+
newsSearchTextProblem,
|
|
179
|
+
noMetalStoriesMessage,
|
|
180
|
+
noStoriesMessage,
|
|
181
|
+
quotedSearchCommand,
|
|
182
|
+
searchNewsStories,
|
|
183
|
+
wordMeansMore,
|
|
184
|
+
} from "./news-search";
|
|
185
|
+
import { formatUtcTime } from "../../utils/utc-time";
|
|
186
|
+
import { DAY_SO_FAR_TEXT, withSessionBar } from "../../time-series/session-bar";
|
|
187
|
+
import { isFiniteNumber } from "../../utils/guards";
|
|
188
|
+
import { exportedFundamentals } from "../../utils/price-earnings";
|
|
189
|
+
import { fundamentalsFreshness, fxPairFreshness, quotesFreshness, rowsFreshness, valuationFreshness } from "../freshness";
|
|
190
|
+
import { fxRateNotes } from "../../plugins/builtin/shared/fx-rate-freshness";
|
|
191
|
+
import { quoteBarNotes } from "../../market-data/quotes/intraday-bar";
|
|
192
|
+
import { depositaryPairText, isReceiptSide, listingDepositaryPair } from "../../market-data/depositary-pair";
|
|
193
|
+
import { isPesoDollarPair, loadPesoDollarRates, pesoDollarRateLines, pesoDollarRatesJson } from "../peso-dollar-rates";
|
|
194
|
+
import { atRegularSessionPrice, regularSessionValuationPrice } from "../../market-data/quotes/regular-session-valuation";
|
|
195
|
+
import { viewMarketCapitalization } from "../../utils/market-capitalization";
|
|
196
|
+
import {
|
|
197
|
+
barHistoryFreshness,
|
|
198
|
+
barResolutionFromDates,
|
|
199
|
+
cloudNewsFreshness,
|
|
200
|
+
cloudRealtimeAccess,
|
|
201
|
+
REPORTED_DATA,
|
|
202
|
+
SEC_FILINGS,
|
|
203
|
+
} from "../../plugins/builtin/shared/report-freshness";
|
|
204
|
+
import {
|
|
205
|
+
earningsDateStatus,
|
|
206
|
+
earningsDetailParts,
|
|
207
|
+
earningsEpsBasis,
|
|
208
|
+
epsBasisName,
|
|
209
|
+
isProvisionalActual,
|
|
210
|
+
quarterLabel,
|
|
211
|
+
showsEpsBasis,
|
|
212
|
+
} from "../../plugins/builtin/shared/earnings-report";
|
|
213
|
+
import {
|
|
214
|
+
calendarEventOf,
|
|
215
|
+
calendarStatus,
|
|
216
|
+
newYorkDay,
|
|
217
|
+
resolveListingEvents,
|
|
218
|
+
withResolvedEarnings,
|
|
219
|
+
type ResolvedEarningsEvent,
|
|
220
|
+
} from "../../plugins/builtin/shared/earnings-calendar";
|
|
221
|
+
import {
|
|
222
|
+
coinListingNotes,
|
|
223
|
+
EXCHANGE_OPTION,
|
|
224
|
+
listingHeading,
|
|
225
|
+
listingIdentity,
|
|
226
|
+
listingTitle,
|
|
227
|
+
listingVenues,
|
|
228
|
+
listingChoiceNotes,
|
|
229
|
+
listingReadNotes,
|
|
230
|
+
failUnchosenListing,
|
|
231
|
+
loadBareVenues,
|
|
232
|
+
loadForListing,
|
|
233
|
+
loadListingQuote,
|
|
234
|
+
loadListingQuoteAnswer,
|
|
235
|
+
listingFateLoader,
|
|
236
|
+
meantQuestion,
|
|
237
|
+
movedSymbolLine,
|
|
238
|
+
ListingArgError,
|
|
239
|
+
requireCliListing,
|
|
240
|
+
resolveCliListing,
|
|
241
|
+
type CliListing,
|
|
242
|
+
type ListingIdentity,
|
|
243
|
+
} from "../listing-arg";
|
|
244
|
+
import { bareListingNote, listingFateNotes, noLongerTradingNote, tradingListing } from "../../tickers/listing-fate";
|
|
245
|
+
import { isYieldIndexSymbol } from "../index-roots";
|
|
246
|
+
import {
|
|
247
|
+
analyzeTaxEquivalent, distributionBasis, loadTreasuryTenors, parseTaxRates, TAX_RATE_HELP_OPTIONS, taxEquivalentJson, type TaxRates,
|
|
248
|
+
} from "../../market-data/tax-equivalent";
|
|
249
|
+
import { CBOE_YIELD_INDEX_CLOSE } from "../../plugins/builtin/shared/treasury-cutoffs";
|
|
250
|
+
import { earningsCalendarFailure, earningsChoiceNote, earningsDateLabel, isListingAbroad, noListingCalendarNote } from "../earnings-calendar";
|
|
251
|
+
import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
|
|
252
|
+
import { providerMissReason } from "../../sources/provider-errors";
|
|
253
|
+
import { isNetworkFailure } from "../../utils/network-failure";
|
|
254
|
+
import { withSavedCopy, type ReportFreshness } from "../pane-functions/freshness";
|
|
255
|
+
import { isNotATickerMessage } from "../not-a-ticker";
|
|
256
|
+
import { primeBoardCoins } from "../board-coins";
|
|
257
|
+
import { suggestUsTicker, type ListingSuggestion } from "../listing-suggestion";
|
|
258
|
+
import { quotedPhrase, splitSymbolArgs, uniqueSecuritySymbols } from "../symbol-args";
|
|
259
|
+
import { isCompanyNameInput } from "../../tickers/company-name-listing";
|
|
260
|
+
import { readNameEntries, readPhraseArgument } from "../name-entries";
|
|
261
|
+
import { readSymbolList, SymbolListError } from "../symbol-list";
|
|
262
|
+
import { notASymbolError } from "../symbol-cells";
|
|
263
|
+
import { cliPerpetualContract, perpetualContractStats, type PerpetualContractPayload } from "../perpetual-contract";
|
|
264
|
+
import { knownGapMessage, subMinuteDataMessage } from "../known-gaps";
|
|
265
|
+
import { phraseSearchReport } from "./search";
|
|
266
|
+
import { secRegistrantMismatchMessage, SecRegistrantMismatchError, areDifferentCompanies } from "../../sources/sec-registrant";
|
|
267
|
+
import { isCryptoPairSymbol, isRoundTheClockCoin } from "../../utils/crypto-pair";
|
|
268
|
+
import { isUsListingExchange } from "../../utils/exchanges";
|
|
269
|
+
import {
|
|
270
|
+
describeFormerIssuer, describeTickerChange, movedAwayChange, startTickerChangeLookup, tickerChangeLine, type FormerIssuer, type TickerChange,
|
|
271
|
+
} from "../../tickers/ticker-changes";
|
|
272
|
+
import { noSecReportsNotice, nonUsSecListingVenue, secFilingItemCodes } from "../../utils/sec";
|
|
273
|
+
import type { MarketContext } from "../types";
|
|
274
|
+
import { windowRows } from "../row-window";
|
|
275
|
+
import { answerQuote, errorMessage, notUsTickerSentence } from "../quote-answer";
|
|
276
|
+
import { crossRate, describeFxConversion, describeFxRate, formatFxRate, FX_USAGE, isUsableRate, parseFxArgs } from "../fx-pair";
|
|
277
|
+
import {
|
|
278
|
+
describeShortRate,
|
|
279
|
+
formatForwardPoints,
|
|
280
|
+
formatRatePercent,
|
|
281
|
+
formatSignedPercent,
|
|
282
|
+
IMPLIED_FORWARD_LABEL,
|
|
283
|
+
impliedForward,
|
|
284
|
+
parseForwardTenor,
|
|
285
|
+
shortRateFor,
|
|
286
|
+
} from "../fx-forward";
|
|
287
|
+
import { unavailableOnServer } from "../../plugins/builtin/shared/cloud-resource";
|
|
288
|
+
|
|
289
|
+
/** `fx`'s forward form, for its usage line. */
|
|
290
|
+
const FX_FORWARD_USAGE = "fx <base>/<quote> --forward <tenor>";
|
|
291
|
+
import {
|
|
292
|
+
holderListFacts,
|
|
293
|
+
holderListPeriod,
|
|
294
|
+
holderShareBasisMarker,
|
|
295
|
+
holderShareBasisNote,
|
|
296
|
+
holderValueBasis,
|
|
297
|
+
moneyColumnHeader,
|
|
298
|
+
nonUsHolderCaveat,
|
|
299
|
+
sharedReportDate,
|
|
300
|
+
} from "../../plugins/builtin/holders/report-header";
|
|
301
|
+
import { formatPercentHeld, holderStakeMarketCap, holderValueCurrency } from "../../plugins/builtin/holders/format";
|
|
302
|
+
import {
|
|
303
|
+
holderReportColumns,
|
|
304
|
+
holderReportDates,
|
|
305
|
+
holderReportNotices,
|
|
306
|
+
holderReportRows,
|
|
307
|
+
holderReportUnits,
|
|
308
|
+
unvaluedHoldersNote,
|
|
309
|
+
} from "../../plugins/builtin/holders/report-rows";
|
|
310
|
+
import { fetchBeneficialOwners } from "../../plugins/builtin/holders/beneficial-client";
|
|
311
|
+
import { fundHoldersAnswer } from "../../plugins/builtin/holders/fund-holders";
|
|
312
|
+
import { centralBankEventsMessage } from "../central-bank-events";
|
|
313
|
+
import { classSharesAsOf, classSharesToday } from "../../plugins/builtin/holders/client";
|
|
314
|
+
import {
|
|
315
|
+
HOLDER_ROWS_MAX,
|
|
316
|
+
indexedHoldersPartialNote,
|
|
317
|
+
indexedHolderUnits,
|
|
318
|
+
indexHolderTicker,
|
|
319
|
+
loadIndexedHolders,
|
|
320
|
+
withIndexedHolders,
|
|
321
|
+
} from "../../plugins/builtin/holders/index-holders";
|
|
322
|
+
import { filingFormMatches, SEC_FILING_FETCH_LIMIT } from "../../plugins/builtin/sec/forms";
|
|
323
|
+
import { currentReportItemsLabel } from "../../utils/sec-filings";
|
|
324
|
+
import { INSIDER_REPORT_FILINGS, loadParsedInsiderFilings } from "../../plugins/builtin/insider/client";
|
|
325
|
+
import { insiderEmptyNotice, loadHomeInsiderReportsNote } from "../../plugins/builtin/insider/issuer";
|
|
326
|
+
import { buildInsiderSummary } from "../../plugins/builtin/insider/model";
|
|
327
|
+
import {
|
|
328
|
+
BENEFICIAL_REPORT_COLUMNS,
|
|
329
|
+
beneficialCoverageNotices,
|
|
330
|
+
beneficialListComplete,
|
|
331
|
+
beneficialListFacts,
|
|
332
|
+
beneficialListUnreadable,
|
|
333
|
+
beneficialReportLegend,
|
|
334
|
+
beneficialRouteForm,
|
|
335
|
+
buildBeneficialReportRows,
|
|
336
|
+
HOLDER_FORMS,
|
|
337
|
+
parseHolderForm,
|
|
338
|
+
type HolderForm,
|
|
339
|
+
} from "../../plugins/builtin/holders/beneficial-report";
|
|
340
|
+
|
|
341
|
+
/** The ranges `history` takes: the chart ranges, and YTD, which runs from the prior year's last close. */
|
|
342
|
+
type HistoryRange = TimeRange | "YTD";
|
|
343
|
+
const HISTORY_RANGES: HistoryRange[] = [...TIME_RANGES.slice(0, TIME_RANGES.indexOf("1Y")), "YTD", ...TIME_RANGES.slice(TIME_RANGES.indexOf("1Y"))];
|
|
344
|
+
const VALID_RANGES = new Set<HistoryRange>(HISTORY_RANGES);
|
|
345
|
+
const VALID_NEWS_FEEDS = new Set<NewsFeed>(["latest", "top", "breaking", "ticker", "sector", "topic"]);
|
|
346
|
+
|
|
347
|
+
type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & {
|
|
348
|
+
error: string | null;
|
|
349
|
+
/** The listing the row is, named from its saved ticker when the quote names nothing. */
|
|
350
|
+
listing?: { name: string | null };
|
|
351
|
+
/** A futures row's contract, terms and latest confirmed settlement, as `ticker` reads them. */
|
|
352
|
+
futuresContract?: FuturesContractTermsPayload;
|
|
353
|
+
/** The quote of the root's front contract, for a futures row that prices another one (NGF27, NG2). */
|
|
354
|
+
futuresFront?: Quote;
|
|
355
|
+
/**
|
|
356
|
+
* A per-MWh gas price (Dutch TTF) in dollars per MMBtu, the unit Henry Hub
|
|
357
|
+
* trades in, converted at the day's rate: one unit of the quote's currency in dollars.
|
|
358
|
+
*/
|
|
359
|
+
usdPerMmbtu?: UsdPerMmbtu;
|
|
360
|
+
/** A perpetual row's market, funding per eight hours and open interest. */
|
|
361
|
+
perpetualContract?: PerpetualContractPayload;
|
|
362
|
+
/** The symbol changed: its issuer moved to another symbol, or its holder had another before. */
|
|
363
|
+
tickerChange?: TickerChange;
|
|
364
|
+
/** The company that last held the symbol merged into another, which trades under its own symbol now (GOGL, CMBT). */
|
|
365
|
+
formerIssuer?: FormerIssuer;
|
|
366
|
+
/** A bare symbol read on a listing abroad that is one slip from a US ticker: the US ticker it was most likely typed for. */
|
|
367
|
+
usSuggestion?: ListingSuggestion;
|
|
368
|
+
};
|
|
369
|
+
type FinancialsCliData = TickerFinancials & {
|
|
370
|
+
symbol: string;
|
|
371
|
+
exchange: string;
|
|
372
|
+
providerId: string | null;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
function parseRange(value: string | undefined, ctx: Parameters<CliCommandDef["execute"]>[1]): HistoryRange {
|
|
376
|
+
const range = (value ?? "1Y").toUpperCase() as HistoryRange;
|
|
377
|
+
if (!VALID_RANGES.has(range)) ctx.fail(`Unknown range "${value}".`, `Use one of ${HISTORY_RANGES.join(", ")}.`);
|
|
378
|
+
return range;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function parseNewsFeed(value: string | undefined): NewsQuery["feed"] | undefined {
|
|
382
|
+
return value && VALID_NEWS_FEEDS.has(value as NewsFeed) ? value as NewsQuery["feed"] : undefined;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* The entries of a command's arguments (`splitSymbolArgs`), each once however
|
|
387
|
+
* its case or share class was written: `AAPL aapl BRK.B BRK-B` is AAPL and
|
|
388
|
+
* BRK.B. An entry with a space, from a comma list or pasted lines, keeps its
|
|
389
|
+
* case: it may be a company's name, which `readSymbols` says.
|
|
390
|
+
*/
|
|
391
|
+
function normalizeSymbols(args: string[]): string[] {
|
|
392
|
+
return uniqueEntries(splitSymbolArgs(args));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Each entry once, a symbol upper-cased and a company's name in its typed case. */
|
|
396
|
+
function uniqueEntries(entries: readonly string[]): string[] {
|
|
397
|
+
return uniqueSecuritySymbols(entries.map((entry) => isCompanyNameInput(entry) ? entry : entry.toUpperCase()));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The symbols to resolve: each entry with a space read as a company's name
|
|
402
|
+
* when search finds one, else as its words when they are symbols
|
|
403
|
+
* (`readNameEntries`), with the repeats that leaves dropped. A name stays whole.
|
|
404
|
+
*/
|
|
405
|
+
async function readSymbols(symbols: readonly string[], dataProvider: Parameters<typeof readNameEntries>[1]): Promise<string[]> {
|
|
406
|
+
return uniqueEntries(await readNameEntries(symbols, dataProvider));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* The symbols typed, of `--file <path>` and of standard input (`-`), read by
|
|
411
|
+
* the reader `watchlist add` uses, and its notes on what it left out (a
|
|
412
|
+
* stray separator, the prices beside the symbols, in one line). Standard
|
|
413
|
+
* input is read only when asked for, so a script that pipes in nothing never
|
|
414
|
+
* waits on it.
|
|
415
|
+
*/
|
|
416
|
+
async function readListedSymbols(
|
|
417
|
+
args: readonly string[],
|
|
418
|
+
file: string | undefined,
|
|
419
|
+
market: MarketContext,
|
|
420
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
421
|
+
usage: string,
|
|
422
|
+
): Promise<{ symbols: string[]; notes: string[] }> {
|
|
423
|
+
try {
|
|
424
|
+
const list = await readSymbolList({
|
|
425
|
+
args, file, stdinPiped: false, readEntries: (entries) => readNameEntries(entries, market.dataProvider),
|
|
426
|
+
});
|
|
427
|
+
// Only numbers, or nothing: the message says what was left out (`Ignored 1.899, a number, not a symbol.`).
|
|
428
|
+
if (list.symbols.length === 0) {
|
|
429
|
+
const where = file && file !== "-" ? file : args.includes("-") ? "standard input" : "what was given";
|
|
430
|
+
ctx.fail([`No symbols in ${where}.`, ...list.notes].join(" "), usage);
|
|
431
|
+
}
|
|
432
|
+
return { symbols: uniqueEntries(list.symbols), notes: list.notes };
|
|
433
|
+
} catch (error) {
|
|
434
|
+
if (error instanceof SymbolListError) ctx.fail(error.message, usage);
|
|
435
|
+
throw error;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** What `quote --csv` and `compare --csv` write, for `help quote` and `help compare`. */
|
|
440
|
+
const QUOTE_CSV_SECTION = {
|
|
441
|
+
title: "CSV",
|
|
442
|
+
lines: [
|
|
443
|
+
"Last, Chg%, Prev Close and each extended print are plain numbers, the currency in Cur and currency_or_unit. A pre-market, after-hours or overnight print's move is in Pre-Market Chg%, After Hours Chg% or Overnight Chg%, and compare's day range in Day Low and Day High, after the columns the export already had.",
|
|
444
|
+
"A row with no figure says why in its status column, never NaN; a symbol with no quote is also named on standard error.",
|
|
445
|
+
],
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
/** `--file` of quote and compare, as `watchlist add` takes it. */
|
|
449
|
+
const LIST_FILE_OPTION = {
|
|
450
|
+
flags: "--file <path>",
|
|
451
|
+
description: "Read the symbols from a CSV or text file, as watchlist add does (- reads standard input)",
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
/** What JSON metadata says about the listing a command resolved. */
|
|
455
|
+
function listingMetadata(identity: ListingIdentity) {
|
|
456
|
+
return { symbol: identity.symbol, exchange: identity.exchange || null, name: identity.name };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Fitted to a narrow width, quote's Updated, Feed, Cur, Chg Basis and session prints move to labelled lines under
|
|
460
|
+
// their row, in that order, before Name wraps: quote never drops a column. What a user types or reads the price by stays whole.
|
|
461
|
+
const QUOTE_LEAD_COLUMNS = [
|
|
462
|
+
{ key: "symbol", header: "Symbol", shrink: false },
|
|
463
|
+
{ key: "name", header: "Name" },
|
|
464
|
+
{
|
|
465
|
+
key: "price",
|
|
466
|
+
header: "Last",
|
|
467
|
+
align: "right" as const,
|
|
468
|
+
// An export writes the number; the row's Cur and currency_or_unit name what it is in.
|
|
469
|
+
value: (row: QuoteRow) => exportFigure(row.rawPrice),
|
|
470
|
+
format: (_value: unknown, row: QuoteRow) => (
|
|
471
|
+
row.error && !row.price
|
|
472
|
+
// A typo is not an outage: say what is wrong with the symbol itself.
|
|
473
|
+
? isNotATickerMessage(row.error) ? cliStyles.warning("not a ticker") : cliStyles.danger("unavailable")
|
|
474
|
+
// A London line adds the pence it trades in, as the exchange and RNS write it, muted as a table cell's note is: £6.308 630.8p.
|
|
475
|
+
: `${yieldLevelCell(row.price, row)}${row.pence ? ` ${cliStyles.muted(row.pence)}` : ""}`
|
|
476
|
+
),
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
key: "changePercent",
|
|
480
|
+
header: "Chg%",
|
|
481
|
+
align: "right" as const,
|
|
482
|
+
format: (value: unknown, row: QuoteRow) => {
|
|
483
|
+
const move = row.yieldIndex ? yieldChangeCell(row) : formatChangePercentCell(value);
|
|
484
|
+
// Keep a rolling move's short label with the value when narrow tables drop Chg Basis.
|
|
485
|
+
return move && row.changeBasis === "24h" ? `${move} ${cliStyles.muted("24h")}` : move;
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
// Text drops a column no row fills, so a table without an extended print stays as narrow as before.
|
|
489
|
+
extendedColumn("PRE"),
|
|
490
|
+
extendedColumn("POST"),
|
|
491
|
+
extendedColumn("OVERNIGHT"),
|
|
492
|
+
// An export keeps the market state (PRE-MKT); the table names the close a pre-market with no trade stands at (Thu close).
|
|
493
|
+
{ key: "session", header: "Session", shrink: false, format: (_value: unknown, row: QuoteRow) => row.sessionLabel },
|
|
494
|
+
];
|
|
495
|
+
|
|
496
|
+
type QuoteRow = ReturnType<typeof quoteRows>[number];
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* What Chg% is measured from, as every change line says it ("vs Fri 16 Oct
|
|
500
|
+
* close", "since 00:00 UTC"): the table sets it after Chg% and names the
|
|
501
|
+
* venue's holidays under it; an export adds it, and the holiday note, after
|
|
502
|
+
* its existing columns, so a column read by position stays where it was.
|
|
503
|
+
*/
|
|
504
|
+
const CHANGE_BASIS_COLUMN = {
|
|
505
|
+
key: "changeBasis", header: "Chg Basis", shrink: false, stackPriority: 2,
|
|
506
|
+
format: (value: unknown) => value === "24h" ? "" : cliStyles.muted(String(value ?? "")),
|
|
507
|
+
};
|
|
508
|
+
const CHANGE_BASIS_EXPORT_COLUMNS = [
|
|
509
|
+
{ key: "changeBasis", header: "Chg Basis" },
|
|
510
|
+
{ key: "changeBasisNote", header: "Chg Basis Note" },
|
|
511
|
+
];
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* The table's columns: Chg Basis follows Chg%, and the note is a line under
|
|
515
|
+
* the table. The figures an export adds (an extended print's move, the day's
|
|
516
|
+
* low and high) are already in the table's own cells.
|
|
517
|
+
*/
|
|
518
|
+
function quoteTextColumns<Column extends { key: string }>(columns: Column[]): Array<Column | typeof CHANGE_BASIS_COLUMN> {
|
|
519
|
+
const exported = new Set([...CHANGE_BASIS_EXPORT_COLUMNS, ...EXTENDED_CHANGE_EXPORT_COLUMNS, ...DAY_RANGE_EXPORT_COLUMNS].map((column) => column.key));
|
|
520
|
+
const kept = columns.filter((column) => !exported.has(column.key));
|
|
521
|
+
const at = kept.findIndex((column) => column.key === "changePercent") + 1;
|
|
522
|
+
return [...kept.slice(0, at), CHANGE_BASIS_COLUMN, ...kept.slice(at)];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** A Cboe yield index level is a yield: `5.24%`, not a price. */
|
|
526
|
+
function yieldLevelCell(value: unknown, row: QuoteRow): string {
|
|
527
|
+
const text = String(value ?? "");
|
|
528
|
+
return row.yieldIndex && text ? `${text}%` : text;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** A yield index moves in basis points: `+1.3bp`, from the real previous close, not a percent of a percent. */
|
|
532
|
+
function yieldChangeCell(row: QuoteRow): string {
|
|
533
|
+
return row.changeBasisPoints == null ? "" : colorBySign(formatBasisPoints(row.changeBasisPoints / 100, 1), row.changeBasisPoints);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/** A figure as an export writes it: the number without float noise, nothing for none. */
|
|
537
|
+
function exportFigure(value: number | null | undefined): number | null {
|
|
538
|
+
return value == null ? null : Number.isFinite(value) ? exportNumber(value) : value;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const EXTENDED_SESSIONS = ["PRE", "POST", "OVERNIGHT"] as const satisfies readonly ExtendedSession[];
|
|
542
|
+
|
|
543
|
+
function extendedKey(session: ExtendedSession): string {
|
|
544
|
+
return session === "PRE" ? "preMarket" : session === "POST" ? "afterHours" : "overnight";
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* The pre-market, after-hours or overnight print and its move from the
|
|
549
|
+
* regular close, in its own column; "mid" when it is a bid and ask midpoint.
|
|
550
|
+
* An export writes the price alone, its move going to `<session> Chg%`
|
|
551
|
+
* (EXTENDED_CHANGE_EXPORT_COLUMNS) and a midpoint to a closing note.
|
|
552
|
+
*/
|
|
553
|
+
function extendedColumn(session: ExtendedSession) {
|
|
554
|
+
const price = (row: QuoteRow) => row.extendedMark ? `${row.extendedPrice} mid` : row.extendedPrice;
|
|
555
|
+
return {
|
|
556
|
+
key: extendedKey(session),
|
|
557
|
+
header: EXTENDED_SESSION_LABELS[session],
|
|
558
|
+
align: "right" as const,
|
|
559
|
+
value: (row: QuoteRow) => row.extendedSession === session ? exportFigure(row.rawExtendedPrice) : null,
|
|
560
|
+
format: (_value: unknown, row: QuoteRow) => (
|
|
561
|
+
row.extendedSession === session && row.extendedPrice
|
|
562
|
+
? [price(row), formatChangePercentCell(row.extendedChangePercent)].join(" ").trim()
|
|
563
|
+
: ""
|
|
564
|
+
),
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Each extended print's move from the regular close, as a number: the
|
|
570
|
+
* columns an export adds after those it had, so a column read by position
|
|
571
|
+
* stays where it was. The table keeps the move beside the price.
|
|
572
|
+
*/
|
|
573
|
+
const EXTENDED_CHANGE_EXPORT_COLUMNS = EXTENDED_SESSIONS.map((session) => ({
|
|
574
|
+
key: `${extendedKey(session)}ChangePercent`,
|
|
575
|
+
header: `${EXTENDED_SESSION_LABELS[session]} Chg%`,
|
|
576
|
+
value: (row: QuoteRow) => row.extendedSession === session ? row.extendedChangePercent : null,
|
|
577
|
+
}));
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Why an exported quote row's figures are empty, for its status column: the
|
|
581
|
+
* symbol's failure as the warning says it, or the figure it has no value for.
|
|
582
|
+
*/
|
|
583
|
+
function quoteExportStatus(row: QuoteRow): string {
|
|
584
|
+
if (row.rawPrice == null || !Number.isFinite(row.rawPrice)) return row.error || "Last: no price";
|
|
585
|
+
if (row.changePercent == null || !Number.isFinite(row.changePercent)) return "Chg%: no previous close";
|
|
586
|
+
return "";
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** The closing note of an export with an extended price that is a bid and ask midpoint: the cell holds the bare number. */
|
|
590
|
+
function extendedMidNotes(data: readonly QuoteCliRecord[]): string[] {
|
|
591
|
+
return data.flatMap((row) => {
|
|
592
|
+
const extended = getExtendedSessionDisplay(row.quote);
|
|
593
|
+
return extended?.mark ? [`${row.target.symbol} ${EXTENDED_SESSION_LABELS[extended.session]} is the bid and ask midpoint; nothing has traded in that session`] : [];
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/** A percent to two decimals, as the table prints it; none for a move that is no number (a zero close). */
|
|
598
|
+
function roundedPercent(value: number | undefined): number | null {
|
|
599
|
+
return value == null || !Number.isFinite(value) ? null : Number(value.toFixed(2));
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/** The quote's currency code, US cents as US¢; an export keeps the code (USX). */
|
|
603
|
+
function currencyCell(value: unknown): string {
|
|
604
|
+
return currencyCodeLabel(typeof value === "string" ? value : "");
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** The session prints of a narrow quote table move under their row after its other columns, before the name. */
|
|
608
|
+
const EXTENDED_KEYS = new Set(["preMarket", "afterHours", "overnight"]);
|
|
609
|
+
|
|
610
|
+
function quoteColumns() {
|
|
611
|
+
return [
|
|
612
|
+
...QUOTE_LEAD_COLUMNS.map((column) => EXTENDED_KEYS.has(column.key) ? { ...column, stackPriority: 1 } : column),
|
|
613
|
+
{ key: "currency", header: "Cur", shrink: false, stackPriority: 3, format: currencyCell },
|
|
614
|
+
// Whether the price is real-time, delayed or stale: a feed state, not where it came from. A stale
|
|
615
|
+
// row says so here, so the closing line's "N of M stale" always points at rows the table marks.
|
|
616
|
+
{
|
|
617
|
+
key: "feed",
|
|
618
|
+
header: "Feed",
|
|
619
|
+
shrink: false,
|
|
620
|
+
stackPriority: 4,
|
|
621
|
+
format: (value: unknown) => value === "stale" ? cliStyles.warning(value) : String(value ?? ""),
|
|
622
|
+
},
|
|
623
|
+
{ key: "updatedAt", header: "Updated", shrink: false, stackPriority: 5 },
|
|
624
|
+
...CHANGE_BASIS_EXPORT_COLUMNS,
|
|
625
|
+
...EXTENDED_CHANGE_EXPORT_COLUMNS,
|
|
626
|
+
];
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** The day's low and high as numbers, after the columns an export already had. */
|
|
630
|
+
const DAY_RANGE_EXPORT_COLUMNS = [
|
|
631
|
+
{ key: "dayLow", header: "Day Low", value: (row: QuoteRow) => exportFigure(row.rawDayLow) },
|
|
632
|
+
{ key: "dayHigh", header: "Day High", value: (row: QuoteRow) => exportFigure(row.rawDayHigh) },
|
|
633
|
+
];
|
|
634
|
+
|
|
635
|
+
function compareColumns() {
|
|
636
|
+
return [
|
|
637
|
+
...QUOTE_LEAD_COLUMNS,
|
|
638
|
+
{
|
|
639
|
+
key: "previousClose", header: "Prev Close", align: "right" as const,
|
|
640
|
+
value: (row: QuoteRow) => exportFigure(row.rawPreviousClose),
|
|
641
|
+
format: (_value: unknown, row: QuoteRow) => yieldLevelCell(row.previousClose, row),
|
|
642
|
+
},
|
|
643
|
+
// A narrow terminal drops these before it cuts the names short.
|
|
644
|
+
// An export writes the range as plain numbers (12.5-13.1), and each end as a number in Day Low and Day High.
|
|
645
|
+
{
|
|
646
|
+
key: "dayRange", header: "Day Range", align: "right" as const, optional: true, dropPriority: 1,
|
|
647
|
+
value: (row: QuoteRow) => row.rawDayLow != null && row.rawDayHigh != null ? `${exportFigure(row.rawDayLow)}-${exportFigure(row.rawDayHigh)}` : "",
|
|
648
|
+
format: (_value: unknown, row: QuoteRow) => row.dayRange,
|
|
649
|
+
},
|
|
650
|
+
{ key: "volume", header: "Volume", align: "right" as const, format: formatCountCell, optional: true, dropPriority: 2 },
|
|
651
|
+
{ key: "currency", header: "Cur", shrink: false, optional: true, dropPriority: 3, format: currencyCell },
|
|
652
|
+
...CHANGE_BASIS_EXPORT_COLUMNS,
|
|
653
|
+
...EXTENDED_CHANGE_EXPORT_COLUMNS,
|
|
654
|
+
...DAY_RANGE_EXPORT_COLUMNS,
|
|
655
|
+
];
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** What the Feed cell says: stale wins over the feed's own live or delayed, as the closing line counts it. */
|
|
659
|
+
function quoteFeedState(quote: QuoteCliRecord["quote"], now: number): "stale" | "live" | "delayed" | "" {
|
|
660
|
+
if (!quote) return "";
|
|
661
|
+
if (isQuoteStaleForCurrentSession(quote, now)) return "stale";
|
|
662
|
+
return quote.dataSource === "live" || quote.dataSource === "delayed" ? quote.dataSource : "";
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/** A hundredth of a cent, one basis point of a dollar peg: the decimals CRYP prints a stablecoin to. */
|
|
666
|
+
const STABLECOIN_PRICE_DIGITS = 4;
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* A coin trading near a dollar, a dollar stablecoin, prints to four decimals
|
|
670
|
+
* either side of par (0.9987, 1.0003), as CRYP does, whatever digits its
|
|
671
|
+
* source sent (0.99874, 0.99968725); below a dollar a coin otherwise keeps up to eight.
|
|
672
|
+
*/
|
|
673
|
+
function nearParCoin(quote: QuoteCliRecord["quote"]): boolean {
|
|
674
|
+
if (!quote || resolveAssetDisplayKind({ assetCategory: quote.instrumentType }) !== "crypto") return false;
|
|
675
|
+
if ((quote.currency ?? "USD").trim().toUpperCase() !== "USD") return false;
|
|
676
|
+
const reference = [quote.previousClose, quote.price].find((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
|
|
677
|
+
return reference != null && reference >= 0.9 && reference < 1.1;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/** What a row's move is measured from, worded as its Chg Basis cell: empty without a move. */
|
|
681
|
+
function rowChangeBasis(result: Pick<QuoteCliRecord, "quote" | "futuresContract">) {
|
|
682
|
+
return quoteChangeBasis(result.quote, { terms: result.futuresContract });
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function basisLabel(result: Pick<QuoteCliRecord, "quote" | "futuresContract">, now: number): string {
|
|
686
|
+
const basis = rowChangeBasis(result);
|
|
687
|
+
return basis ? changeBasisLabel(basis, now) : "";
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* The lines under the table naming the days a row's venue was closed since
|
|
692
|
+
* the close its move is from ("HKEX closed Mon 19 Oct, holiday"), once each.
|
|
693
|
+
*/
|
|
694
|
+
function changeBasisNotes(results: QuoteCliRecord[], now = Date.now()): string[] {
|
|
695
|
+
return [...new Set(results.flatMap((result) => changeBasisNote(rowChangeBasis(result), now) ?? []))];
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/** The quote as `--json` gives it: `previousCloseDate`, the exchange-local date of its previous close, follows `previousClose`. */
|
|
699
|
+
function withPreviousCloseDate(quote: Quote | null | undefined): (Quote & { previousCloseDate?: string | null }) | null {
|
|
700
|
+
if (!quote) return null;
|
|
701
|
+
const previousCloseDate = quotePreviousCloseDate(quote);
|
|
702
|
+
const entries = Object.entries(quote).filter(([key]) => key !== "previousCloseDate");
|
|
703
|
+
const at = entries.findIndex(([key]) => key === "previousClose");
|
|
704
|
+
entries.splice(at < 0 ? entries.length : at + 1, 0, ["previousCloseDate", previousCloseDate]);
|
|
705
|
+
return Object.fromEntries(entries) as Quote & { previousCloseDate?: string | null };
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** An explicit missing comparison wins over any leftover numeric move in a CLI quote. */
|
|
709
|
+
function quoteSessionFields(quote: Quote | null | undefined) {
|
|
710
|
+
const fields = getQuoteSessionFields(quote);
|
|
711
|
+
return quote?.changeBasis === "none" ? { ...fields, change: null, changePercent: null } : fields;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
|
|
715
|
+
return results.map((result) => {
|
|
716
|
+
const quote = result.quote;
|
|
717
|
+
// Last and Chg% are the regular session, as `ticker` and the quote monitor read it; a
|
|
718
|
+
// pre-market or after-hours print is its own column, measured from that session's close.
|
|
719
|
+
const display = quoteSessionFields(quote);
|
|
720
|
+
const extended = getExtendedSessionDisplay(quote);
|
|
721
|
+
const yieldIndex = isYieldIndexSymbol(result.target.symbol);
|
|
722
|
+
// An index level is in points, not in the currency its members trade in.
|
|
723
|
+
const indexPoints = isIndexLevel(quote?.instrumentType);
|
|
724
|
+
// Pad to two decimals so a column lines up, but never past the currency's minor unit (JPY has none).
|
|
725
|
+
// Points have no minor unit, so a yen-listed index still pads to two.
|
|
726
|
+
const options = {
|
|
727
|
+
...quoteFormatOptions(quote),
|
|
728
|
+
minimumFractionDigits: indexPoints ? 2 : currencyPriceMinDigits(quote?.currency),
|
|
729
|
+
...nearParCoin(quote) ? { fixedFractionDigits: STABLECOIN_PRICE_DIGITS } : {},
|
|
730
|
+
};
|
|
731
|
+
const price = (value: number | null | undefined) => (
|
|
732
|
+
quote && value != null
|
|
733
|
+
? indexPoints ? formatMarketPrice(value, options) : formatMarketPriceWithCurrency(value, quote.currency, options)
|
|
734
|
+
: ""
|
|
735
|
+
);
|
|
736
|
+
// A price in cents says what it is per, as the contract terms word it: 314.95¢/lb.
|
|
737
|
+
const per = display.price != null ? futuresPricePer(result.futuresContract, quote?.currency) : "";
|
|
738
|
+
const previousClose = display.price != null && display.change != null ? display.price - display.change : quote?.previousClose;
|
|
739
|
+
const last = price(display.price);
|
|
740
|
+
return {
|
|
741
|
+
symbol: result.target.symbol,
|
|
742
|
+
name: quote?.name || result.listing?.name || "",
|
|
743
|
+
price: `${last}${per}`,
|
|
744
|
+
pence: penceFigure(display.price, quote, /\.(\d+)/.exec(last)?.[1]?.length ?? 0),
|
|
745
|
+
rawPrice: display.price,
|
|
746
|
+
priceBasis: quote?.priceBasis ?? null,
|
|
747
|
+
instrumentType: quote?.instrumentType ?? null,
|
|
748
|
+
change: display.change,
|
|
749
|
+
changePercent: roundedPercent(display.changePercent ?? undefined),
|
|
750
|
+
// A Cboe yield index is read as a yield in percent and moves in basis points.
|
|
751
|
+
yieldIndex,
|
|
752
|
+
changeBasisPoints: yieldIndex && display.change != null ? toBasisPoints(display.change, 1) : null,
|
|
753
|
+
extendedSession: extended?.session ?? null,
|
|
754
|
+
extendedPrice: price(extended?.price),
|
|
755
|
+
rawExtendedPrice: extended?.price ?? null,
|
|
756
|
+
extendedChange: extended?.change ?? null,
|
|
757
|
+
extendedChangePercent: roundedPercent(extended?.changePercent),
|
|
758
|
+
// The extended price is the session's bid and ask midpoint, nothing having traded in it.
|
|
759
|
+
extendedMark: extended?.mark === true,
|
|
760
|
+
session: quoteSessionStateLabel(quote, now) ?? "",
|
|
761
|
+
sessionLabel: quoteSessionLabel(quote, now) ?? "",
|
|
762
|
+
// The close the shown move is measured from: the one before the session Last is.
|
|
763
|
+
previousClose: price(previousClose),
|
|
764
|
+
rawPreviousClose: previousClose ?? null,
|
|
765
|
+
rawDayLow: quote?.low != null && quote.high != null ? quote.low : null,
|
|
766
|
+
rawDayHigh: quote?.low != null && quote.high != null ? quote.high : null,
|
|
767
|
+
dayRange: quote?.low != null && quote.high != null
|
|
768
|
+
? indexPoints ? `${price(quote.low)}-${price(quote.high)}` : formatPriceRange(quote.low, quote.high, quote.currency, options, "-")
|
|
769
|
+
: "",
|
|
770
|
+
// An index's 0 is volume nobody counts, and its level has no currency.
|
|
771
|
+
volume: reportedQuoteVolume(quote),
|
|
772
|
+
currency: indexPoints ? "" : quote?.currency ?? "",
|
|
773
|
+
providerId: quote?.providerId ?? "",
|
|
774
|
+
source: quote?.dataSource ?? quote?.providerId ?? "",
|
|
775
|
+
feed: quoteFeedState(quote, now),
|
|
776
|
+
// When the price Last shows was observed: in a pre-market nothing has traded in, the close it stands at.
|
|
777
|
+
updatedAt: quote?.lastUpdated ? new Date(quoteHeadlineTime(quote)).toISOString() : "",
|
|
778
|
+
error: result.error ?? "",
|
|
779
|
+
// Last, so a row read by position keeps every field where it was.
|
|
780
|
+
changeBasis: basisLabel(result, now),
|
|
781
|
+
changeBasisNote: changeBasisNote(rowChangeBasis(result), now) ?? "",
|
|
782
|
+
};
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function financialStatementRows(financials: FinancialsCliData) {
|
|
787
|
+
return financials.annualStatements.map((statement) => ({
|
|
788
|
+
date: statement.date,
|
|
789
|
+
revenue: statement.totalRevenue ?? statement.operatingRevenue ?? null,
|
|
790
|
+
grossProfit: statement.grossProfit ?? null,
|
|
791
|
+
operatingIncome: statement.operatingIncome ?? null,
|
|
792
|
+
netIncome: statement.netIncome ?? statement.netIncomeCommonStockholders ?? null,
|
|
793
|
+
eps: statement.eps ?? statement.basicEps ?? null,
|
|
794
|
+
currency: statement.currency?.trim() || financials.financialCurrency?.trim() || "",
|
|
795
|
+
}))
|
|
796
|
+
// A provider row holding only balance-sheet remnants has nothing for these columns.
|
|
797
|
+
.filter((row) => [row.revenue, row.grossProfit, row.operatingIncome, row.netIncome, row.eps].some((value) => value != null))
|
|
798
|
+
.slice(0, 8);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function newsRows(articles: NewsArticle[]) {
|
|
802
|
+
return articles.map((article) => ({
|
|
803
|
+
title: article.title,
|
|
804
|
+
source: article.source,
|
|
805
|
+
publishedAt: isoDate(article.publishedAt),
|
|
806
|
+
topic: article.topic,
|
|
807
|
+
tickers: article.tickers.join(","),
|
|
808
|
+
url: article.url,
|
|
809
|
+
importance: article.importance,
|
|
810
|
+
breaking: article.isBreaking,
|
|
811
|
+
}));
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function filingRows(filings: SecFilingItem[]) {
|
|
815
|
+
return filings.map((filing) => ({
|
|
816
|
+
form: filing.form,
|
|
817
|
+
filingDate: isoDate(filing.filingDate).slice(0, 10),
|
|
818
|
+
companyName: filing.companyName ?? "",
|
|
819
|
+
accessionNumber: filing.accessionNumber,
|
|
820
|
+
url: filing.primaryDocumentUrl ?? filing.filingUrl,
|
|
821
|
+
items: secFilingItemCodes(filing.items) ?? "",
|
|
822
|
+
// A current report's items by number and name: the results 8-K reads apart from another filed that day.
|
|
823
|
+
itemsLabel: currentReportItemsLabel(filing.items) ?? "",
|
|
824
|
+
}));
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/** The filings as JSON gives them, with a current report's items named beside their codes. */
|
|
828
|
+
function filingsWithItemLabels(filings: SecFilingItem[]) {
|
|
829
|
+
return filings.map((filing) => {
|
|
830
|
+
const itemsLabel = currentReportItemsLabel(filing.items);
|
|
831
|
+
return itemsLabel ? { ...filing, itemsLabel } : filing;
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/** The summary over the ratings: the upside from Last, and the price and time it is measured from, as `fn ANR` gives them. */
|
|
836
|
+
function analystSummary(data: AnalystResearchData, quote: Quote | null): string {
|
|
837
|
+
const target = data.priceTarget;
|
|
838
|
+
const currency = analystTargetCurrency(data);
|
|
839
|
+
const { upside, reference } = analystUpside(data, quote);
|
|
840
|
+
const analysts = recommendationTotal(data);
|
|
841
|
+
const mix = formatRecommendationMix(data);
|
|
842
|
+
const entries: Array<[string, string]> = [
|
|
843
|
+
["Average Target", formatAnalystPrice(target?.average, currency)],
|
|
844
|
+
["Upside", upside == null ? "-" : formatPercent(upside)],
|
|
845
|
+
["Upside vs", upside == null ? "-" : formatAnalystReference(reference, currency)],
|
|
846
|
+
["Low / Median / High", target && [target.low, target.median, target.high].some((value) => value != null)
|
|
847
|
+
? [target.low, target.median, target.high].map((value) => formatAnalystPrice(value, currency)).join(" / ")
|
|
848
|
+
: "-"],
|
|
849
|
+
["Rating", formatRatingLabel(data.recommendationRating)],
|
|
850
|
+
["Analysts", analysts == null ? "-" : String(analysts)],
|
|
851
|
+
["Mix", mix],
|
|
852
|
+
];
|
|
853
|
+
const populated = entries.filter(([, value]) => value !== "-");
|
|
854
|
+
return populated.length > 0 ? renderStats(populated) : "";
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function analystRows(data: AnalystResearchData) {
|
|
858
|
+
return data.ratings.map((rating) => ({
|
|
859
|
+
date: rating.date,
|
|
860
|
+
firm: rating.firm,
|
|
861
|
+
action: rating.action ?? "",
|
|
862
|
+
current: rating.current ?? "",
|
|
863
|
+
prior: rating.prior ?? "",
|
|
864
|
+
target: rating.currentPriceTarget ?? null,
|
|
865
|
+
}));
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/** Provider values carry binary floating-point noise such as 0.26940000000000003. */
|
|
869
|
+
function cleanDecimal(value: number | undefined): string {
|
|
870
|
+
return value == null || !Number.isFinite(value) ? "" : String(Number(value.toFixed(6)));
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function corporateActionRows(data: CorporateActionsData) {
|
|
874
|
+
const currency = /^[A-Z]{3}$/.test(data.currency ?? "") ? data.currency! : null;
|
|
875
|
+
// EPS is in the reporting currency, which can differ from the listing's dividends (Tencent: CNY vs HKD).
|
|
876
|
+
// An upcoming estimate carries no unit of its own; it shares the one every reported quarter states.
|
|
877
|
+
const reportedCurrencies = new Set(data.earnings.map((event) => event.currency?.trim()).filter(Boolean));
|
|
878
|
+
const earningsCurrency = reportedCurrencies.size === 1 ? [...reportedCurrencies][0] : undefined;
|
|
879
|
+
// To the cent, as ERN and EE show it; a consensus average carries more digits.
|
|
880
|
+
const eps = (value: number | undefined, unit: string | undefined) => (
|
|
881
|
+
value == null || !Number.isFinite(value) ? "-"
|
|
882
|
+
: !unit ? formatNumber(value, 2)
|
|
883
|
+
: /^[A-Z]{3}$/.test(unit) ? formatCurrency(value, unit, 2) : `${formatNumber(value, 2)} ${unit}`
|
|
884
|
+
);
|
|
885
|
+
return [
|
|
886
|
+
...data.earnings.map((event) => {
|
|
887
|
+
const unit = event.currency?.trim() || earningsCurrency;
|
|
888
|
+
const quarter = quarterLabel(event.fiscalPeriod);
|
|
889
|
+
const basis = earningsEpsBasis(event.epsBasis);
|
|
890
|
+
// History rows are keyed by fiscal quarter end, upcoming ones by announcement date.
|
|
891
|
+
const note = [event.dateType === "fiscal-period-end" ? "period end" : null, quarter === "-" ? null : quarter].filter(Boolean).join(", ");
|
|
892
|
+
return {
|
|
893
|
+
type: "earnings",
|
|
894
|
+
date: event.date,
|
|
895
|
+
// The basis, beat, date status and analysts are the words EE uses for the same row.
|
|
896
|
+
detail: [event.epsActual == null ? `est ${eps(event.epsEstimate, unit)}` : `eps ${eps(event.epsActual, unit)}`,
|
|
897
|
+
...earningsDetailParts(event)].join(", ") + (note ? ` (${note})` : ""),
|
|
898
|
+
epsActual: event.epsActual,
|
|
899
|
+
epsEstimate: event.epsEstimate,
|
|
900
|
+
epsBasis: showsEpsBasis({ epsActual: event.epsActual, epsBasis: basis }) ? epsBasisName(basis) : undefined,
|
|
901
|
+
...(isProvisionalActual(event) ? { epsProvisional: true } : {}),
|
|
902
|
+
epsAnalysts: event.epsAnalysts,
|
|
903
|
+
dateStatus: earningsDateStatus(event.dateStatus) ?? undefined,
|
|
904
|
+
fiscalPeriod: event.fiscalPeriod,
|
|
905
|
+
};
|
|
906
|
+
}),
|
|
907
|
+
...data.dividends.map((event) => ({
|
|
908
|
+
type: "dividend",
|
|
909
|
+
date: event.exDate,
|
|
910
|
+
detail: currency ? formatDistributionAmount(event.amount, currency) : cleanDecimal(event.amount),
|
|
911
|
+
})),
|
|
912
|
+
// Declared and unpaid, at its ex-date beside the paid rows, never one of them.
|
|
913
|
+
...declaredDividends(data).map((event) => ({
|
|
914
|
+
type: "dividend",
|
|
915
|
+
date: event.exDate,
|
|
916
|
+
detail: `declared ${currency ? formatDistributionAmount(event.amount, currency) : cleanDecimal(event.amount)}`
|
|
917
|
+
+ (event.payDate ? `, payable ${event.payDate}` : ""),
|
|
918
|
+
})),
|
|
919
|
+
...data.splits.map((event) => ({
|
|
920
|
+
type: "split",
|
|
921
|
+
date: event.date,
|
|
922
|
+
detail: event.description ?? `${event.fromFactor ?? ""}:${event.toFactor ?? ""}`,
|
|
923
|
+
})),
|
|
924
|
+
].sort((left, right) => right.date.localeCompare(left.date));
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* After a US open, a chain whose latest trade predates it still carries the
|
|
929
|
+
* prior session's quotes and volume. The delayed feed lags the open by about
|
|
930
|
+
* fifteen minutes, so this is expected early in the session.
|
|
931
|
+
*/
|
|
932
|
+
function priorSessionChainWarning(chain: OptionsChain, exchange: string, now: number): string | null {
|
|
933
|
+
const observed = chain.asOf ? Date.parse(chain.asOf) : Number.NaN;
|
|
934
|
+
if (!Number.isFinite(observed) || (exchange && !getPublishedUsEquityCalendarYears(exchange))) return null;
|
|
935
|
+
const today = new Intl.DateTimeFormat("en-CA", { timeZone: "America/New_York" }).format(now);
|
|
936
|
+
const session = getPublishedUsEquitySession(exchange || "NYSE", today);
|
|
937
|
+
if (session?.kind !== "session" || now < session.open || observed >= session.open) return null;
|
|
938
|
+
return `No option trades this session yet (last trade ${chain.asOf})`;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/** Per-share earnings to the cent, as reported; consensus averages carry more digits. */
|
|
942
|
+
function formatEpsCell(value: unknown): string {
|
|
943
|
+
return typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : "";
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function earningsRows(events: ResolvedEarningsEvent[]) {
|
|
947
|
+
return events.map((event) => ({
|
|
948
|
+
symbol: event.symbol,
|
|
949
|
+
name: event.name,
|
|
950
|
+
date: isoDate(event.earningsDate).slice(0, 10),
|
|
951
|
+
// Out, announced by the company, or projected from its past reports.
|
|
952
|
+
status: earningsDateLabel(event.dateStatus),
|
|
953
|
+
timing: event.timing,
|
|
954
|
+
epsEstimate: event.epsEstimate,
|
|
955
|
+
epsActual: event.epsActual,
|
|
956
|
+
revenueEstimate: event.revenueEstimate,
|
|
957
|
+
revenueActual: event.revenueActual,
|
|
958
|
+
}));
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/** Symbols at most whose venues a command looks up to name the company each bare one went to. */
|
|
962
|
+
const MAX_CHOICE_LOOKUPS = 10;
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* The line `quote` prints beside a symbol that changed, as `history` and
|
|
966
|
+
* `ticker` word it: a company that moved, with nothing new listed there,
|
|
967
|
+
* names the quote of its new symbol ("HFC became DINO on 2022-03-15:
|
|
968
|
+
* `gloom quote DINO`"); its old line's last price stays in the row.
|
|
969
|
+
*/
|
|
970
|
+
function quoteChangeLine(change: TickerChange | null | undefined, formerIssuer?: FormerIssuer | null): string | null {
|
|
971
|
+
const moved = movedAwayChange(change);
|
|
972
|
+
// One line per symbol: a company that left it, else the company that merged away from it (GOGL).
|
|
973
|
+
return (moved ? movedSymbolLine(moved, `gloom quote ${moved.tradesAs}`) : tickerChangeLine(change)) ?? formerIssuer?.note ?? null;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1], commandName: "quote" | "compare") {
|
|
977
|
+
const args = [...rawArgs];
|
|
978
|
+
const usage = `Usage: gloom ${commandName} <symbol...> [--file <path>]`;
|
|
979
|
+
const exchange = takeOption(args, "--exchange") ?? "";
|
|
980
|
+
// A list from a file or standard input (`-`), read as `watchlist add` reads one.
|
|
981
|
+
const file = args.some((arg) => arg === "--file" || arg.startsWith("--file=")) ? takeOption(args, "--file") ?? "" : undefined;
|
|
982
|
+
const listed = file != null || args.includes("-");
|
|
983
|
+
const entries = normalizeSymbols(args);
|
|
984
|
+
if (entries.length === 0 && !listed) ctx.fail(usage);
|
|
985
|
+
// One quoted argument with spaces is a company's name, a pasted line of tickers, or a phrase to search for.
|
|
986
|
+
const phrase = exchange || listed ? null : quotedPhrase(args);
|
|
987
|
+
|
|
988
|
+
await withMarketData(ctx, async (market) => {
|
|
989
|
+
const read = phrase ? await readPhraseArgument(phrase, market.dataProvider) : null;
|
|
990
|
+
if (read?.kind === "phrase") return answerPhrase(read.phrase, market, ctx);
|
|
991
|
+
// Typed or from a file, one reader names what it left out: a stray separator, the prices beside the symbols.
|
|
992
|
+
const list = read ? null : await readListedSymbols(args, file, market, ctx, usage);
|
|
993
|
+
const symbols = read?.kind === "name" ? [read.name]
|
|
994
|
+
: read?.kind === "symbols" ? uniqueEntries(read.symbols)
|
|
995
|
+
: list!.symbols;
|
|
996
|
+
// What cannot be a symbol (N/A, $$$, a price alone) is never looked up; one line names it.
|
|
997
|
+
const refused = symbols.flatMap((symbol) => notASymbolError(symbol) ?? []);
|
|
998
|
+
if (refused.length === symbols.length) {
|
|
999
|
+
ctx.fail(refused.length === 1 ? refused[0]!.message : `Nothing to quote: ${refusedList(refused)} are not symbols.`, usage);
|
|
1000
|
+
}
|
|
1001
|
+
// With several symbols, --exchange is for the ones that name no exchange of their own, and one that names no
|
|
1002
|
+
// listing (EQT:NYSE:US) is named in a line while the others are quoted.
|
|
1003
|
+
const resolvedListings = await Promise.all(symbols.filter((symbol) => !notASymbolError(symbol)).map(async (symbol) => {
|
|
1004
|
+
if (symbols.length === 1) return { symbol, listing: await requireCliListing(symbol, exchange, market, ctx) };
|
|
1005
|
+
try {
|
|
1006
|
+
return { symbol, listing: await resolveCliListing(symbol, exchange, market, { ownExchangeWins: true }) };
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
if (error instanceof ListingArgError) return { symbol, error };
|
|
1009
|
+
throw error;
|
|
1010
|
+
}
|
|
1011
|
+
}));
|
|
1012
|
+
const unread = resolvedListings.flatMap((entry) => "error" in entry && entry.error ? [{ symbol: entry.symbol, error: entry.error }] : []);
|
|
1013
|
+
const listings = resolvedListings.flatMap((entry) => "listing" in entry && entry.listing ? [entry.listing] : []);
|
|
1014
|
+
if (listings.length === 0) {
|
|
1015
|
+
const [first] = unread;
|
|
1016
|
+
ctx.fail(first!.error.message, [...unread.slice(1).map(unreadLine), ...refused.length > 0 ? [refusedLine(refused)] : []].join("\n") || first!.error.details);
|
|
1017
|
+
}
|
|
1018
|
+
const targets: QuoteSubscriptionTarget[] = listings.map((listing) => ({ symbol: listing.request.symbol, exchange: listing.request.exchange }));
|
|
1019
|
+
// A bare symbol's venues load beside its quote, to name the company it was read as when several list it.
|
|
1020
|
+
// Each costs a search, so a long list (a portfolio check, not a lookup) names none.
|
|
1021
|
+
const venues = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
|
|
1022
|
+
: listings.map((listing) => [listing, loadBareVenues(listing, market)] as const));
|
|
1023
|
+
// So is whether a symbol changed (FB is now META), for the same lists.
|
|
1024
|
+
const changes = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
|
|
1025
|
+
: listings.map((listing) => [listing, startTickerChangeLookup(market.dataProvider, listing.symbol, listing.exchange)] as const));
|
|
1026
|
+
// The service's quote of the moment, not each form's local copy from whenever it was last asked for:
|
|
1027
|
+
// BRK.B saved on NYSE, BRK-B and BRK.B:NYSE print one quote. A local copy answers only offline.
|
|
1028
|
+
// Each row prints what its move is from (Chg Basis).
|
|
1029
|
+
const results = await market.dataProvider.getQuotesBatch(targets, { forceRefresh: ctx.cliOptions.refresh, revalidate: true, datesChangeBasis: true });
|
|
1030
|
+
const listingOf = (result: QuoteBatchResult, index: number) => listings[targets.indexOf(result.target)] ?? listings[index]!;
|
|
1031
|
+
const asked = results.map(listingOf);
|
|
1032
|
+
const request = { command: commandName, noun: "quote" };
|
|
1033
|
+
const answers = await Promise.all(results.map((result, index) => {
|
|
1034
|
+
const listing = asked[index]!;
|
|
1035
|
+
return answerQuote(result, listing, market, request, ctx.cliOptions.refresh, venues.get(listing) ?? null, { datesChangeBasis: true });
|
|
1036
|
+
}));
|
|
1037
|
+
const now = Date.now();
|
|
1038
|
+
const symbolChanges = await Promise.all(answers.map(async ({ result }, index) => {
|
|
1039
|
+
const listing = asked[index]!;
|
|
1040
|
+
// A bare symbol read on a listing abroad (SXR8 -> XETRA) is not the US symbol the changes are of.
|
|
1041
|
+
if (result.quote && bareListingNote(listing, result.quote)) return { change: null, formerIssuer: null };
|
|
1042
|
+
const payload = await changes.get(listing)?.settle() ?? null;
|
|
1043
|
+
const current = tradingListing(result.quote, now);
|
|
1044
|
+
return { change: describeTickerChange(listing.symbol, payload, current), formerIssuer: describeFormerIssuer(listing.symbol, payload, current) };
|
|
1045
|
+
}));
|
|
1046
|
+
const tickerChanges = symbolChanges.map(({ change }) => change);
|
|
1047
|
+
const formerIssuers = symbolChanges.map(({ formerIssuer }) => formerIssuer);
|
|
1048
|
+
const failed = answers[0]?.failure;
|
|
1049
|
+
if (symbols.length === 1 && failed) {
|
|
1050
|
+
ctx.fail(failed.message, [quoteChangeLine(tickerChanges[0], formerIssuers[0]), failed.details].filter(Boolean).join("\n") || undefined);
|
|
1051
|
+
}
|
|
1052
|
+
// A futures quote adds its dated settlement and contract terms, as `ticker` prints them; `compare` stays a table.
|
|
1053
|
+
const contracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
|
|
1054
|
+
? cliFuturesContract(result.quote.symbol || result.target.symbol, result.quote.instrumentType)
|
|
1055
|
+
: null));
|
|
1056
|
+
// A contract that is not its root's front (NGF27, NG2) prints the front's own price beside it, read in one batch.
|
|
1057
|
+
const fronts = await loadFuturesFrontQuotes(contracts, market.dataProvider);
|
|
1058
|
+
// A per-MWh gas price (TTF) adds its dollars per MMBtu at the day's rate, to read beside Henry Hub.
|
|
1059
|
+
const gasInUsd = await Promise.all(answers.map(({ result }, index) => loadUsdPerMmbtu(
|
|
1060
|
+
getRegularSessionDisplay(result.quote)?.price, contracts[index], result.quote?.currency, (currency) => market.dataProvider.getExchangeRate(currency))));
|
|
1061
|
+
// A perpetual adds its funding per eight hours and open interest, the figures PERP lists beside its mark.
|
|
1062
|
+
const perpetualContracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
|
|
1063
|
+
? cliPerpetualContract(result.quote)
|
|
1064
|
+
: null));
|
|
1065
|
+
// A bare symbol quoted on a listing abroad (APPL, Aptus Pharma on BSE) that is one slip from a US ticker (AAPL) says so.
|
|
1066
|
+
const usSuggestions = await Promise.all(answers.map(({ result, listing, notes }) => {
|
|
1067
|
+
const venue = result.quote && !listing.saved ? bareListingNote(listing, result.quote) : null;
|
|
1068
|
+
return venue && notes.includes(venue) ? suggestUsTicker(listing.symbol, market) : null;
|
|
1069
|
+
}));
|
|
1070
|
+
// Each row names its listing by key (SAN:EPA) and company, so the table needs no line above it.
|
|
1071
|
+
const data = answers.map(({ result, listing, failure }, index) => ({
|
|
1072
|
+
target: result.target,
|
|
1073
|
+
listing: listingMetadata(listingIdentity(listing, result.quote)),
|
|
1074
|
+
// The figures the table shows, beside the quote as the source sent it.
|
|
1075
|
+
...quoteSessionFields(result.quote),
|
|
1076
|
+
// What `change` is measured from, with the label and holiday note the table shows.
|
|
1077
|
+
changeBasis: changeBasisJson(quoteChangeBasis(result.quote, { terms: contracts[index] }), now),
|
|
1078
|
+
quote: withPreviousCloseDate(result.quote),
|
|
1079
|
+
...contracts[index] ? { futuresContract: contracts[index] } : {},
|
|
1080
|
+
...fronts[index] ? { futuresFront: fronts[index] } : {},
|
|
1081
|
+
...gasInUsd[index] ? { usdPerMmbtu: gasInUsd[index] } : {},
|
|
1082
|
+
...perpetualContracts[index] ? { perpetualContract: perpetualContracts[index] } : {},
|
|
1083
|
+
...usSuggestions[index] ? { usSuggestion: usSuggestions[index] } : {},
|
|
1084
|
+
error: failure?.oneLine ?? errorMessage(result.error),
|
|
1085
|
+
...yieldIndexFields(result),
|
|
1086
|
+
...tickerChanges[index] ? { tickerChange: tickerChanges[index] } : {},
|
|
1087
|
+
...formerIssuers[index] ? { formerIssuer: formerIssuers[index] } : {},
|
|
1088
|
+
}));
|
|
1089
|
+
const notes = [
|
|
1090
|
+
...list?.notes ?? [],
|
|
1091
|
+
...refused.length > 0 ? [refusedLine(refused)] : [],
|
|
1092
|
+
...unread.map(unreadLine),
|
|
1093
|
+
// FB now lists another security; the company that was FB trades as META. A symbol a
|
|
1094
|
+
// company renamed into (META) is what was typed: its change is in the row, not here.
|
|
1095
|
+
// GOGL was Golden Ocean, which merged into CMB.Tech; a fund lists there now.
|
|
1096
|
+
...tickerChanges.flatMap((change, index) => quoteChangeLine(change, formerIssuers[index]) ?? []),
|
|
1097
|
+
// Which listing a bare symbol was read as, and the index a bare root is not, are in no row.
|
|
1098
|
+
...answers.flatMap((answer) => answer.notes),
|
|
1099
|
+
// A row that stopped trading long ago reads as a quote that is merely stale, and one with
|
|
1100
|
+
// nothing printed in a session the calendar has open (a feed that stalled, a typhoon day) says since when.
|
|
1101
|
+
...data.flatMap((row) => {
|
|
1102
|
+
if (!row.quote) return [];
|
|
1103
|
+
const label = `${row.target.symbol}${row.listing.name ? ` (${row.listing.name})` : ""}`;
|
|
1104
|
+
const ended = noLongerTradingNote(label, row.quote, now);
|
|
1105
|
+
const quiet = ended ? null : noPrintsTodayText(row.quote, now);
|
|
1106
|
+
return [ended ?? (quiet ? `${label}: ${quiet}` : []), quoteBarNotes(label, row.quote, now)].flat();
|
|
1107
|
+
}),
|
|
1108
|
+
// A receipt and its home line each say the ratio, as `ticker` does.
|
|
1109
|
+
...data.flatMap((row) => {
|
|
1110
|
+
const pair = listingDepositaryPair({ quote: row.quote });
|
|
1111
|
+
return pair ? [`${row.target.symbol}: ${depositaryPairText(pair, isReceiptSide({ quote: row.quote }))}.`] : [];
|
|
1112
|
+
}),
|
|
1113
|
+
// A cell shows only "unavailable", and an exported row only its status, so stderr names each failure; the JSON rows carry each reason.
|
|
1114
|
+
...ctx.cliOptions.format !== "json" ? quoteNotes(data) : [],
|
|
1115
|
+
];
|
|
1116
|
+
const columns = commandName === "compare" ? compareColumns() : quoteColumns();
|
|
1117
|
+
const basisNotes = changeBasisNotes(data, now);
|
|
1118
|
+
const midNotes = extendedMidNotes(data);
|
|
1119
|
+
if (ctx.cliOptions.format !== "json") await primeBoardCoins(notes);
|
|
1120
|
+
// Only yield indices: their change is in bp, so the text header drops the percent sign (CSV keeps the percent it holds).
|
|
1121
|
+
const yieldsOnly = data.length > 0 && data.every((row) => isYieldIndexSymbol(row.target.symbol));
|
|
1122
|
+
ctx.printResult({
|
|
1123
|
+
data,
|
|
1124
|
+
warnings: notes.length > 0 ? notes : undefined,
|
|
1125
|
+
// A Cboe yield index stops at its 15:00 ET close, before the Treasury's curve is taken: the line says so, so its
|
|
1126
|
+
// close does not read as a contradiction of DGS10 or GC for the same day.
|
|
1127
|
+
// Dated by the quotes as served, which say whether one is the saved copy.
|
|
1128
|
+
freshness: withSavedCopy(
|
|
1129
|
+
// A table across markets (US and London lines) dates each market's session and says when each opens.
|
|
1130
|
+
quotesFreshness(data.map((row) => row.quote), { datesEachVenue: true, ...yieldsOnly ? { schedule: `Cboe close ${CBOE_YIELD_INDEX_CLOSE}` } : {} }),
|
|
1131
|
+
answers.map(({ result }) => result.quote),
|
|
1132
|
+
),
|
|
1133
|
+
}, {
|
|
1134
|
+
rows: quoteRows,
|
|
1135
|
+
// An index row's Cur is blank, its level having no currency; the export names what it is in instead.
|
|
1136
|
+
exportIdentity: (row) => ({
|
|
1137
|
+
symbolOrSeries: row.symbol,
|
|
1138
|
+
currencyOrUnit: row.currency || (row.yieldIndex ? "pct" : isIndexLevel(row.instrumentType) ? INDEX_POINTS_EXPORT_UNIT : null),
|
|
1139
|
+
}),
|
|
1140
|
+
// A row with no price or move says why in its status column, as the warnings do.
|
|
1141
|
+
exportStatus: quoteExportStatus,
|
|
1142
|
+
...(midNotes.length > 0 ? { exportNotes: midNotes } : {}),
|
|
1143
|
+
details: quoteDetailsText,
|
|
1144
|
+
...(basisNotes.length ? { footnote: basisNotes.join("\n") } : {}),
|
|
1145
|
+
columns,
|
|
1146
|
+
textColumns: quoteTextColumns(yieldsOnly
|
|
1147
|
+
? columns.map((column) => column.key === "changePercent" ? { ...column, header: "Chg" } : column)
|
|
1148
|
+
: columns),
|
|
1149
|
+
});
|
|
1150
|
+
// The table and the warnings name each symbol it found nothing for; with none found, the exit status says so too.
|
|
1151
|
+
if (data.every((row) => !row.quote)) process.exitCode = CLI_EXIT_CODES.error;
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/** The entries refused before any lookup, named as written: `$$$, N/A`, split by semicolons when one has a comma. */
|
|
1156
|
+
function refusedList(refused: ReadonlyArray<{ input: string }>): string {
|
|
1157
|
+
const inputs = refused.map((error) => error.input.trim());
|
|
1158
|
+
return inputs.join(inputs.some((input) => input.includes(",")) ? "; " : ", ");
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/** The one line a quote prints for what it did not look up: `Not a symbol, not quoted: $$$, N/A.` */
|
|
1162
|
+
function refusedLine(refused: ReadonlyArray<{ input: string }>): string {
|
|
1163
|
+
return `Not ${refused.length === 1 ? "a symbol" : "symbols"}, not quoted: ${refusedList(refused)}.`;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/** A symbol of a list that names no listing: `Not quoted: EQT:NYSE:US (Unknown exchange US for EQT:NYSE).` */
|
|
1167
|
+
function unreadLine({ symbol, error }: { symbol: string; error: ListingArgError }): string {
|
|
1168
|
+
return `Not quoted: ${symbol} (${error.message.replace(/\.$/, "")}).`;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* `quote "10 year yield"`: a phrase that is no ticker and no company's name
|
|
1173
|
+
* is answered with what search finds for it and the catalog, never quoted
|
|
1174
|
+
* word by word (10, YEAR, YIELD). A script asked for a quote, so structured
|
|
1175
|
+
* output fails with where to look instead.
|
|
1176
|
+
*/
|
|
1177
|
+
async function answerPhrase(phrase: string, market: MarketContext, ctx: Parameters<CliCommandDef["execute"]>[1]): Promise<void> {
|
|
1178
|
+
const said = `"${phrase}" is not a ticker or a company's name.`;
|
|
1179
|
+
if (ctx.cliOptions.format !== "text") {
|
|
1180
|
+
ctx.fail(said, [knownGapMessage(phrase), `Search for it: gloom search "${phrase}"`, `Functions and data: gloom catalog "${phrase}"`]
|
|
1181
|
+
.filter(Boolean).join("\n"));
|
|
1182
|
+
}
|
|
1183
|
+
process.exitCode = CLI_EXIT_CODES.error;
|
|
1184
|
+
if (ctx.cliOptions.quiet) return;
|
|
1185
|
+
// Searched first: a busy search fails on its own, without the line saying the phrase names nothing.
|
|
1186
|
+
const report = await phraseSearchReport(phrase, market, ctx.plugins);
|
|
1187
|
+
console.error(`${cliStyles.warning("warning:")} ${said} What search finds for it:`);
|
|
1188
|
+
console.log(report);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Under the table: each futures row's Settle line and Contract block, then the
|
|
1193
|
+
* line for a symbol that is not a US ticker though it is one slip from one.
|
|
1194
|
+
*/
|
|
1195
|
+
function quoteDetailsText(data: QuoteCliRecord[]): string {
|
|
1196
|
+
return [quoteFuturesContractText(data), ...data.flatMap((row) => notUsTickerLine(row) ?? [])].filter(Boolean).join("\n\n");
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* "APPL is not a US ticker: it is Aptus Pharma Limited on BSE (INR). Did you
|
|
1201
|
+
* mean AAPL (Apple Inc.)?" in the warning colour, for a row that has one.
|
|
1202
|
+
*/
|
|
1203
|
+
function notUsTickerLine(row: QuoteCliRecord): string | null {
|
|
1204
|
+
const sentence = notUsTickerSentence(row);
|
|
1205
|
+
return sentence && cliStyles.warning(sentence);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
/** Each futures row's front contract quote, when its terms name a front other than its own contract; null otherwise. */
|
|
1209
|
+
async function loadFuturesFrontQuotes(
|
|
1210
|
+
contracts: ReadonlyArray<FuturesContractTermsPayload | null>,
|
|
1211
|
+
provider: Pick<MarketContext["dataProvider"], "getQuotesBatch">,
|
|
1212
|
+
): Promise<Array<Quote | null>> {
|
|
1213
|
+
const symbols = [...new Set(contracts.flatMap((terms) => terms?.front?.contractSymbol ?? []))];
|
|
1214
|
+
if (symbols.length === 0) return contracts.map(() => null);
|
|
1215
|
+
const results = await provider.getQuotesBatch(symbols.map((symbol) => ({ symbol, exchange: "" })))
|
|
1216
|
+
.catch(logFailure("cli.commands-market", "Read futures front quotes failed", []));
|
|
1217
|
+
const quotes = new Map(results.flatMap((result) => result.quote ? [[result.target.symbol, result.quote] as const] : []));
|
|
1218
|
+
return contracts.map((terms) => terms?.front ? quotes.get(terms.front.contractSymbol) ?? null : null);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Each futures row's Settle line and Contract block, worded as `ticker`
|
|
1223
|
+
* words them, with how a continuous symbol or a generic rolls, the front it
|
|
1224
|
+
* is not and the day's most active contract after the contract it prices.
|
|
1225
|
+
* Several symbols name theirs in the block's title.
|
|
1226
|
+
*/
|
|
1227
|
+
function quoteFuturesContractText(data: QuoteCliRecord[]): string {
|
|
1228
|
+
return data.flatMap((row) => {
|
|
1229
|
+
if (row.perpetualContract) {
|
|
1230
|
+
const stats = perpetualContractStats(row.perpetualContract);
|
|
1231
|
+
return [data.length > 1 ? `${renderSection(`${row.target.symbol} Contract`)}\n${renderStats(stats)}` : `${renderSection("Contract")}\n${renderStats(stats)}`];
|
|
1232
|
+
}
|
|
1233
|
+
const terms = row.futuresContract;
|
|
1234
|
+
if (!terms || !row.quote) return [];
|
|
1235
|
+
const settle: [string, string] = ["Settle", quoteSettleText(row.quote, terms)];
|
|
1236
|
+
// The move Chg% gives, in the quote's money and with what it is measured from: "vs Fri settle".
|
|
1237
|
+
const session = getRegularSessionDisplay(row.quote);
|
|
1238
|
+
const change: Array<[string, string]> = session?.change != null && Number.isFinite(session.change)
|
|
1239
|
+
? [["Change", quoteChangeText(row.quote, session, withCurrencyMinorDigits(quoteFormatOptions(row.quote, "FUTURE"), row.quote.currency), terms)]]
|
|
1240
|
+
: [];
|
|
1241
|
+
// European gas beside Henry Hub: "≈ $30.832/MMBtu at EUR/USD 1.1650", a conversion that says so.
|
|
1242
|
+
const inUsd = row.usdPerMmbtu;
|
|
1243
|
+
if (inUsd) change.push(["In USD", usdPerMmbtuText(inUsd)]);
|
|
1244
|
+
const metrics = futuresContractMetrics(terms, row.quote.symbol || row.target.symbol);
|
|
1245
|
+
const rolls = futuresRollLines(terms, row.futuresFront).map((line): [string, string] => [line.label, line.value]);
|
|
1246
|
+
const at = futuresContractLines(terms, row.quote.symbol || row.target.symbol)[0]?.key === "contract" ? 1 : 0;
|
|
1247
|
+
const contract = [...metrics.slice(0, at), ...rolls, ...metrics.slice(at)];
|
|
1248
|
+
if (data.length > 1) return [`${renderSection(`${row.target.symbol} Contract`)}\n${renderStats([settle, ...change, ...contract])}`];
|
|
1249
|
+
return [[renderStats([settle, ...change]), ...contract.length ? ["", renderSection("Contract"), renderStats(contract)] : []].join("\n")];
|
|
1250
|
+
}).join("\n\n");
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* What a Cboe yield index adds to its JSON row: the level is a yield in percent
|
|
1255
|
+
* and its change since the previous close is in basis points (the quote's own
|
|
1256
|
+
* `change` is the same move in percentage points).
|
|
1257
|
+
*/
|
|
1258
|
+
function yieldIndexFields(result: QuoteBatchResult): { unit?: "percent"; changeBasisPoints?: number } {
|
|
1259
|
+
if (!isYieldIndexSymbol(result.target.symbol)) return {};
|
|
1260
|
+
const change = getRegularSessionDisplay(result.quote)?.change;
|
|
1261
|
+
return { unit: "percent", ...(change == null ? {} : { changeBasisPoints: toBasisPoints(change, 1) }) };
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* A crypto pair that will not load points at the crypto board. Structured
|
|
1266
|
+
* formats keep the error as thrown, and so does no connection at all, which
|
|
1267
|
+
* the board cannot get past either.
|
|
1268
|
+
*/
|
|
1269
|
+
function failHistory(error: unknown, symbol: string, ctx: Parameters<CliCommandDef["execute"]>[1]): never {
|
|
1270
|
+
if (ctx.cliOptions.format !== "text" || !isCryptoPairSymbol(symbol) || isNetworkFailure(error)) throw error;
|
|
1271
|
+
return ctx.fail(errorMessage(error) ?? `No history available for ${symbol}`, CRYPTO_BOARD_HINT);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
const HISTORY_USAGE = "history <symbol...> [--range <range> | --from <year|date> [--to <year|date>]]";
|
|
1275
|
+
const HISTORY_ALL_RANGE = `ALL gives the full history the source has, up to ${historyAllRangeYears()} years`;
|
|
1276
|
+
const HISTORY_YTD_RANGE = "YTD starts at the prior year's last close, the one year-to-date returns are measured from";
|
|
1277
|
+
/** Options people reach for to ask for dates, which history answers with --from and --to (or --start and --end). */
|
|
1278
|
+
const DATE_WINDOW_OPTIONS = new Set(["--since", "--until", "--date", "--start-date", "--end-date"]);
|
|
1279
|
+
/** Options people reach for to pick a bar size, which history derives from the range. */
|
|
1280
|
+
const BAR_SIZE_OPTIONS = new Set(["--interval", "--resolution", "--bar", "--bars", "--bar-size", "--granularity", "--timeframe", "--frequency", "--freq", "--tick", "--ticks"]);
|
|
1281
|
+
|
|
1282
|
+
/** The symbol `history` was given: the first word that is not an option or the value after one. */
|
|
1283
|
+
function historySymbolArg(args: readonly string[]): string | undefined {
|
|
1284
|
+
return args.find((arg, index) => !arg.startsWith("-")
|
|
1285
|
+
&& !(index > 0 && args[index - 1]!.startsWith("--") && !args[index - 1]!.includes("=")));
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/** What people type for quarterly figures, which `financials` (fiscal years) does not take. */
|
|
1289
|
+
const QUARTERLY_OPTIONS = new Set(["--quarterly", "--quarter", "--quarters", "--qtr", "--period", "--periods", "--frequency", "--freq", "--interval"]);
|
|
1290
|
+
|
|
1291
|
+
/** Options before the symbol whose next word is their value: `financials`'s own, and the quarterly ones that take one. */
|
|
1292
|
+
const FINANCIALS_VALUE_OPTIONS = new Set(["--exchange", "--section", "--period", "--periods", "--frequency", "--freq", "--interval"]);
|
|
1293
|
+
|
|
1294
|
+
/** The symbol `financials` was given, wherever it sits: `--quarterly WFC` is WFC, `--period quarterly WFC` too. */
|
|
1295
|
+
function financialsSymbolArg(args: readonly string[]): string | undefined {
|
|
1296
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1297
|
+
const arg = args[index]!;
|
|
1298
|
+
if (!arg.startsWith("-")) return arg;
|
|
1299
|
+
if (!arg.includes("=") && FINANCIALS_VALUE_OPTIONS.has(arg)) index += 1;
|
|
1300
|
+
}
|
|
1301
|
+
return undefined;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/** `financials --quarterly` points at the functions that show quarters: GF a metric over time, FA the statements. */
|
|
1305
|
+
function financialsOptionHint(flag: string, args: readonly string[]): string | null {
|
|
1306
|
+
if (!QUARTERLY_OPTIONS.has(flag)) return null;
|
|
1307
|
+
const symbol = financialsSymbolArg(args) ?? "<symbol>";
|
|
1308
|
+
return `financials lists fiscal years. For quarters: gloom fn GF ${symbol} --period quarterly --periods 40 (one metric at a time), `
|
|
1309
|
+
+ `or gloom fn FA ${symbol} --period quarterly (the statements).`;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function historyOptionHint(flag: string, args: readonly string[]): string | null {
|
|
1313
|
+
if (DATE_WINDOW_OPTIONS.has(flag)) {
|
|
1314
|
+
return `history takes dates as --from and --to (or --start and --end), each a year or a date (--from 2007 --to 2009), or a --range: ${HISTORY_RANGES.join(", ")}.`;
|
|
1315
|
+
}
|
|
1316
|
+
if (BAR_SIZE_OPTIONS.has(flag)) return `history serves each --range in one bar size. ${subMinuteDataMessage(historySymbolArg(args))}`;
|
|
1317
|
+
return null;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/** "Asked from 1990-01-01, data starts 1995-01-03 (first available bar)" when the bars start over a week into the window. */
|
|
1321
|
+
function windowHistoryNotes(window: PriceWindow, rows: readonly { date: string }[]): string[] {
|
|
1322
|
+
const first = rows[0]?.date.slice(0, 10);
|
|
1323
|
+
if (!first) return [];
|
|
1324
|
+
return Date.parse(`${first}T00:00:00Z`) - Date.parse(`${window.from}T00:00:00Z`) > 7 * 86_400_000
|
|
1325
|
+
? [`Asked from ${window.from}, data starts ${first} (first available bar)`] : [];
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/** "Asked YTD, data starts 2026-03-05 (first available bar)" when no bar reaches the prior year's last close. */
|
|
1329
|
+
function ytdHistoryNotes(rows: readonly { date: string }[]): string[] {
|
|
1330
|
+
const first = rows[0]?.date.slice(0, 10);
|
|
1331
|
+
const last = rows.at(-1)?.date.slice(0, 10);
|
|
1332
|
+
return first && last && first > `${Number(last.slice(0, 4)) - 1}-12-31` ? [`Asked YTD, data starts ${first} (first available bar)`] : [];
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/** What `history` was asked for: a range, or a window of dates in its place. */
|
|
1336
|
+
interface HistoryAsk {
|
|
1337
|
+
range: HistoryRange;
|
|
1338
|
+
window: PriceWindow | null;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/** One listing's bars as `history` prints them, with what its heading, warnings and closing line say. */
|
|
1342
|
+
interface ListingHistory {
|
|
1343
|
+
listing: CliListing;
|
|
1344
|
+
identity: ListingIdentity;
|
|
1345
|
+
unit: HistoryUnit;
|
|
1346
|
+
data: HistoryRow[];
|
|
1347
|
+
resolution: PriceHistoryResult["resolution"];
|
|
1348
|
+
interval: HistoryRow["interval"];
|
|
1349
|
+
decimals: number;
|
|
1350
|
+
/** Intraday bars, which print in UTC as times. */
|
|
1351
|
+
intraday: boolean;
|
|
1352
|
+
volume: HistoryVolume | null;
|
|
1353
|
+
freshness: ReportFreshness | undefined;
|
|
1354
|
+
notes: string[];
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/** The command a listing that cannot be served is pointed back at: `history`, `history --range 5Y`. */
|
|
1358
|
+
function historyCommand({ range, window }: HistoryAsk): string {
|
|
1359
|
+
// A window that runs to today retries to the day it is run on, not the one it was first run on.
|
|
1360
|
+
return window ? `history --from ${window.from}${isPastPriceWindow(window) ? ` --to ${window.to}` : ""}` : range === "1Y" ? "history" : `history --range ${range}`;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
/**
|
|
1364
|
+
* One listing's bars, dated and checked as `history` prints them. Without
|
|
1365
|
+
* `lookups` it does not look up the other venues of a bare symbol or whether
|
|
1366
|
+
* its company moved, which a long list skips, as `quote` does.
|
|
1367
|
+
*/
|
|
1368
|
+
async function loadListingHistory(
|
|
1369
|
+
listing: CliListing,
|
|
1370
|
+
market: MarketContext,
|
|
1371
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
1372
|
+
ask: HistoryAsk,
|
|
1373
|
+
{ lookups }: { lookups: boolean },
|
|
1374
|
+
): Promise<ListingHistory> {
|
|
1375
|
+
const { range, window } = ask;
|
|
1376
|
+
// YTD reads the year's daily bars and starts at the last one on or before 31 Dec.
|
|
1377
|
+
const loadRange: TimeRange = range === "YTD" ? "1Y" : range;
|
|
1378
|
+
const venues = lookups ? loadBareVenues(listing, market) : null;
|
|
1379
|
+
const { symbol, exchange } = listing.request;
|
|
1380
|
+
const context = { cacheMode: ctx.cliOptions.refresh ? "refresh" as const : "default" as const };
|
|
1381
|
+
const load = () => window ? loadPriceWindowHistory(market.dataProvider, symbol, exchange, window, context)
|
|
1382
|
+
: market.dataProvider.getPriceHistoryWithMetadata
|
|
1383
|
+
? market.dataProvider.getPriceHistoryWithMetadata(symbol, exchange, loadRange, context)
|
|
1384
|
+
: market.dataProvider.getPriceHistory(symbol, exchange, loadRange, context).then((points): PriceHistoryResult => ({ points, resolution: null }));
|
|
1385
|
+
const quoteAnswer = loadListingQuoteAnswer(market.dataProvider, listing);
|
|
1386
|
+
const quoted = quoteAnswer.then((answer) => answer.quote);
|
|
1387
|
+
const fate = lookups ? listingFateLoader(listing, market, quoted) : null;
|
|
1388
|
+
const request = { command: historyCommand(ask), noun: "history", ...(fate ? { fate } : {}) };
|
|
1389
|
+
const [loaded, quote] = await Promise.all([
|
|
1390
|
+
loadForListing(listing, market, ctx, load, (loaded) => loaded.points.length === 0, request)
|
|
1391
|
+
.catch((error) => failHistory(error, listing.key, ctx)),
|
|
1392
|
+
quoted,
|
|
1393
|
+
]);
|
|
1394
|
+
// A bare symbol several companies list is never served from the one the data service picked, in a range or a window:
|
|
1395
|
+
// it asks for an exchange as `quote` does, and in a list that is the symbol's row alone.
|
|
1396
|
+
await failUnchosenListing(listing, market, ctx, request, (await quoteAnswer).error);
|
|
1397
|
+
const { resolution } = loaded;
|
|
1398
|
+
// While the session trades the day's bar is the quote's, whatever range the history was cut for (withSessionBar).
|
|
1399
|
+
// A window that ended before today has no session in progress.
|
|
1400
|
+
const points = window && isPastPriceWindow(window) ? loaded.points
|
|
1401
|
+
: withSessionBar(loaded.points, quote, { now: Date.now(), venue: quote?.listingExchangeName || exchange || undefined });
|
|
1402
|
+
const fromQuote = points !== loaded.points;
|
|
1403
|
+
const identity = listingIdentity(listing, quote);
|
|
1404
|
+
// Without a quote currency, the listing metadata research reads (as `ticker` does) may still state it.
|
|
1405
|
+
const listed = quote?.currency ? quote : await market.dataProvider.getQuoteMetadata?.(symbol, exchange).catch(logFailure("cli.commands-market", "Read quote metadata for history failed", null));
|
|
1406
|
+
const unit = historyUnit(listing.key, listed ?? quote);
|
|
1407
|
+
const traded = {
|
|
1408
|
+
listing: listing.key, exchange: quote?.listingExchangeName || exchange || undefined, instrumentType: quote?.instrumentType,
|
|
1409
|
+
};
|
|
1410
|
+
// A coin's volume is traded value in its quote currency: said in every row and on the status line.
|
|
1411
|
+
const volume = historyVolume(unit, traded);
|
|
1412
|
+
// Spot FX has no exchange to count its trades: a source's 0 is no volume, printed as missing.
|
|
1413
|
+
const pair = quote?.instrumentType === "CURRENCY" || isCurrencyPairListing(symbol, exchange);
|
|
1414
|
+
// A London line's bars were priced in pence: cleaned there, and printed to the pence decimals `quote` shows.
|
|
1415
|
+
const priceDivisor = quotedPriceDivisor(quote);
|
|
1416
|
+
const rows = historyRows(pair ? points.map(({ volume: _volume, ...point }) => point) : points, resolution, unit.currency, unit.unit, volume, priceDivisor);
|
|
1417
|
+
// Bars stop arriving on the listing's own trading days, not New York's.
|
|
1418
|
+
const venue = quote?.listingExchangeName || exchange || undefined;
|
|
1419
|
+
const served = range === "YTD" && !window ? rows.slice(Math.max(0, yearToDateStartIndex(rows.map((row) => row.date), rows[0]?.interval, venue))) : rows;
|
|
1420
|
+
// October's monthly bar on 16 Oct runs through the 15th: marked partial, and the status line says how far.
|
|
1421
|
+
const forming = formingPeriodBar(served, venue);
|
|
1422
|
+
// A complete one is dated by the day its last session closed, the day its close is from.
|
|
1423
|
+
const closedDay = forming ? null : closedPeriodBarDay(served, venue);
|
|
1424
|
+
const data = forming?.rows ?? served;
|
|
1425
|
+
const interval = data[0]?.interval ?? null;
|
|
1426
|
+
// Intraday bars print in UTC, as the charts and time and sales label them, not the host zone.
|
|
1427
|
+
const intraday = data.some((row) => row.date.length > 10);
|
|
1428
|
+
// A bar history: dated by its last bar, stale once bars of its size stop arriving.
|
|
1429
|
+
// The day's own bar, while its session trades, is not a close: dated by the quote it was read from.
|
|
1430
|
+
const dayToDate = fromQuote && interval === "1d" && data.at(-1)?.partial === true;
|
|
1431
|
+
// The source that served the bars, a broker or another source when Gloom Cloud could not.
|
|
1432
|
+
const source = loaded.sourceName ? { source: loaded.sourceName } : {};
|
|
1433
|
+
// A window that closed before today is the history asked for: no later bar makes it stale.
|
|
1434
|
+
const freshness = withSavedCopy(window && isPastPriceWindow(window) ? rowsFreshness(data, {
|
|
1435
|
+
...source,
|
|
1436
|
+
status: "not-a-feed", basis: `${interval ? BAR_INTERVAL_LABELS[interval] : "price"} bars, ${priceWindowText(window)}`, observedKey: "date", oldest: null,
|
|
1437
|
+
}) : rowsFreshness(data, {
|
|
1438
|
+
...source,
|
|
1439
|
+
...barHistoryFreshness(intraday ? null : barResolutionFromDates(data.map((row) => row.date))),
|
|
1440
|
+
observedKey: "date",
|
|
1441
|
+
oldest: null,
|
|
1442
|
+
cadenceExchange: venue,
|
|
1443
|
+
...(forming ? { periodToDate: forming.periodToDate } : closedDay ? { asOf: closedDay } : {}),
|
|
1444
|
+
...(dayToDate ? { basis: "daily bars", ...(quote && Number.isFinite(quote.lastUpdated) ? { asOf: new Date(quote.lastUpdated).toISOString() } : {}) } : {}),
|
|
1445
|
+
...(volume || dayToDate ? { note: [volume ? historyVolumeText(volume, interval) : null, dayToDate ? `the newest bar is ${DAY_SO_FAR_TEXT}` : null].filter(Boolean).join("; ") } : {}),
|
|
1446
|
+
// The rows are drawn from the bars served, which say whether they are the saved copy.
|
|
1447
|
+
}), loaded);
|
|
1448
|
+
const gaps = historyGapNote(data, traded);
|
|
1449
|
+
const coverage = window ? historyCoverageNotice(loaded.coverageStart, Date.parse(`${window.from}T00:00:00Z`), loaded.coverageNote)
|
|
1450
|
+
: range === "YTD" ? null : historyCoverageWarning(range, loaded);
|
|
1451
|
+
const notes = [
|
|
1452
|
+
// A window the source has no bar in (a typo, a weekend, a listing that had not started) is said, not an empty table alone.
|
|
1453
|
+
...data.length === 0 ? [emptyHistoryNote(listing.key, ask)] : [],
|
|
1454
|
+
// FB's bars are the fund listed there now; META's from 2021 are from when it was FB.
|
|
1455
|
+
...fate ? listingFateNotes(await fate(), { served: data.length > 0, firstDate: data[0]?.date }) : [],
|
|
1456
|
+
...lookups ? await listingChoiceNotes(listing, market, quote, venues) : [],
|
|
1457
|
+
// ALL says where the source's bars start (BTC-USD's in 2014-09), unless the coverage line does.
|
|
1458
|
+
...window ? [...windowHistoryNotes(window, data), coverage ?? []]
|
|
1459
|
+
: range === "YTD" ? ytdHistoryNotes(data) : [...historyNotes(range, interval, data[0]?.date, Date.now(), coverage != null), coverage ?? []],
|
|
1460
|
+
// The status line says how far a bar still forming runs; the note is for the bars it does not name.
|
|
1461
|
+
gaps ?? [], historyJumpNote(data) ?? [], historyFlagNote(data) ?? [], historyMissingVolumeNote(data) ?? [],
|
|
1462
|
+
historyVenueVolumeNote(data) ?? [],
|
|
1463
|
+
historyBarStatusNote(forming ? served : data, { tradingDay: venueTradingDay(venue, Date.now()) }) ?? [],
|
|
1464
|
+
].flat();
|
|
1465
|
+
return {
|
|
1466
|
+
listing, identity, unit, data, resolution, interval, intraday, volume, freshness, notes,
|
|
1467
|
+
decimals: historyPriceDecimals(data, listing.saved?.metadata.assetCategory, priceDivisor),
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
/** What a listing with no bar for what was asked says, in a warning and in a list's status column. */
|
|
1472
|
+
function emptyHistoryNote(symbol: string, { range, window }: HistoryAsk): string {
|
|
1473
|
+
return window ? `No bars for ${symbol} from ${priceWindowText(window)}.` : `No bars for ${symbol} in ${range}.`;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
/** A bar's prices to the decimals its history prints them to. */
|
|
1477
|
+
function historyPrice(decimals: number) {
|
|
1478
|
+
return (value: unknown) => typeof value === "number" ? value.toFixed(decimals) : "";
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/** The columns a history table prints: Time in UTC for intraday bars, prices to `price`'s decimals. */
|
|
1482
|
+
function historyTextColumns(intraday: boolean, price: (value: unknown, row: Record<string, unknown>) => string): CliResultColumn[] {
|
|
1483
|
+
return [
|
|
1484
|
+
intraday
|
|
1485
|
+
? { key: "date", header: "Time", format: (value) => typeof value === "string" ? formatUtcTime(value) : "" }
|
|
1486
|
+
: { key: "date", header: "Date" },
|
|
1487
|
+
{ key: "open", header: "Open", align: "right", format: price },
|
|
1488
|
+
{ key: "high", header: "High", align: "right", format: price },
|
|
1489
|
+
{ key: "low", header: "Low", align: "right", format: price },
|
|
1490
|
+
{ key: "close", header: "Close", align: "right", format: price },
|
|
1491
|
+
{ key: "volume", header: "Volume", align: "right", format: formatCountCell },
|
|
1492
|
+
];
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* The volume a table prints: a bar with one venue's count in place of the
|
|
1497
|
+
* market's shows it beside the venue, never as its volume, and a bar without
|
|
1498
|
+
* volume in a history that reports it prints as missing, never as 0 or blank.
|
|
1499
|
+
*/
|
|
1500
|
+
function historyVolumeCell(missingVolume: (row: Record<string, unknown>) => string) {
|
|
1501
|
+
return (value: unknown, row: Record<string, unknown>) => formatCountCell(value)
|
|
1502
|
+
|| (typeof row.venueVolume === "number"
|
|
1503
|
+
? cliStyles.muted(`${formatCountCell(row.venueVolume)} ${typeof row.volumeVenue === "string" ? row.volumeVenue : ""}`.trim())
|
|
1504
|
+
: missingVolume(row));
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
/** Text marks a bar still forming, rebuilt from hourly bars or closing before its settlement beside its flag; exports keep them as columns. */
|
|
1508
|
+
const HISTORY_FLAG_TEXT_COLUMN: CliResultColumn = {
|
|
1509
|
+
key: "flag",
|
|
1510
|
+
header: "Flag",
|
|
1511
|
+
format: (value, row) => [
|
|
1512
|
+
typeof value === "string" ? cliStyles.warning(value) : "",
|
|
1513
|
+
row.partial ? cliStyles.muted("partial") : "",
|
|
1514
|
+
row.filled ? cliStyles.muted("filled") : "",
|
|
1515
|
+
row.unsettled ? cliStyles.muted("unsettled") : "",
|
|
1516
|
+
typeof row.status === "string" ? cliStyles.warning(row.status) : "",
|
|
1517
|
+
].filter(Boolean).join(" "),
|
|
1518
|
+
};
|
|
1519
|
+
|
|
1520
|
+
/** The dates `--from` and `--to` (or `--start` and `--end`) name, each given once. */
|
|
1521
|
+
function historyWindowOption(args: string[], rangeOption: string | undefined, ctx: Parameters<CliCommandDef["execute"]>[1]): PriceWindow | null {
|
|
1522
|
+
const bound = (flag: string, alias: string) => {
|
|
1523
|
+
const value = takeOption(args, flag);
|
|
1524
|
+
const aliased = takeOption(args, alias);
|
|
1525
|
+
if (value != null && aliased != null) ctx.fail(`${alias} is ${flag}; give one of them.`, `Usage: gloom ${HISTORY_USAGE}`);
|
|
1526
|
+
return { value: value ?? aliased, flag: aliased != null ? alias : flag };
|
|
1527
|
+
};
|
|
1528
|
+
const typedFrom = bound("--from", "--start");
|
|
1529
|
+
const typedTo = bound("--to", "--end");
|
|
1530
|
+
// Every message names the options as typed: an --end alone asks for a --start, not a --from.
|
|
1531
|
+
const from = { ...typedFrom, flag: typedFrom.value == null && typedTo.flag === "--end" ? "--start" : typedFrom.flag };
|
|
1532
|
+
const to = { ...typedTo, flag: typedTo.value == null && from.flag === "--start" ? "--end" : typedTo.flag };
|
|
1533
|
+
let window: PriceWindow | null;
|
|
1534
|
+
try {
|
|
1535
|
+
window = readPriceWindow(from.value, to.value, new Date(), undefined, { from: from.flag, to: to.flag });
|
|
1536
|
+
} catch (error) {
|
|
1537
|
+
return ctx.fail(errorMessage(error) ?? `Invalid ${from.flag} or ${to.flag}.`, `Usage: gloom ${HISTORY_USAGE}`);
|
|
1538
|
+
}
|
|
1539
|
+
if (window && rangeOption != null) ctx.fail(`${from.flag} replaces --range; give one of them.`, `Usage: gloom ${HISTORY_USAGE}`);
|
|
1540
|
+
return window;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/** How many symbols of a list load at once: enough that a few hundred take a minute or two, not one call after another. */
|
|
1544
|
+
const HISTORY_LIST_CONCURRENCY = 8;
|
|
1545
|
+
|
|
1546
|
+
async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
1547
|
+
const args = [...rawArgs];
|
|
1548
|
+
const rangeOption = takeOption(args, "--range");
|
|
1549
|
+
// --from 2007 --to 2009: those years' bars, daily up to five years, in place of a range ending now.
|
|
1550
|
+
const window = historyWindowOption(args, rangeOption, ctx);
|
|
1551
|
+
const ask: HistoryAsk = { range: parseRange(rangeOption, ctx), window };
|
|
1552
|
+
const requestedExchange = takeOption(args, "--exchange");
|
|
1553
|
+
// A list from a file or standard input (`-`), or several symbols typed (AAPL MSFT, AAPL,MSFT), is one long table.
|
|
1554
|
+
// One argument with spaces stays one, as before: it may be a company's name.
|
|
1555
|
+
const file = args.some((arg) => arg === "--file" || arg.startsWith("--file=")) ? takeOption(args, "--file") ?? "" : undefined;
|
|
1556
|
+
if (file != null || args.includes("-") || uniqueEntries(args).length > 1 || args.some((arg) => /[,;]/.test(arg))) {
|
|
1557
|
+
return runHistoryList(args, file, requestedExchange, ask, ctx);
|
|
1558
|
+
}
|
|
1559
|
+
const raw = requireOneArg(args, HISTORY_USAGE, "symbol", ctx);
|
|
1560
|
+
await withMarketData(ctx, async (market) => {
|
|
1561
|
+
const listing = await requireCliListing(raw, requestedExchange, market, ctx);
|
|
1562
|
+
const history = await loadListingHistory(listing, market, ctx, ask, { lookups: true });
|
|
1563
|
+
const { identity, unit, data, interval, intraday, volume, freshness, notes } = history;
|
|
1564
|
+
const price = historyPrice(history.decimals);
|
|
1565
|
+
// A bar without volume in a history that reports it prints as missing.
|
|
1566
|
+
const missingVolume = historyReportsVolume(data) ? MISSING_VOLUME_TEXT : "";
|
|
1567
|
+
const textColumns = historyTextColumns(intraday, price).map((column) => column.key !== "volume" ? column
|
|
1568
|
+
: { ...column, format: historyVolumeCell(() => missingVolume) });
|
|
1569
|
+
// The largest fall from a close to a later one in the bars shown, said under the heading.
|
|
1570
|
+
const drawdown = maxDrawdown(data);
|
|
1571
|
+
ctx.printResult({
|
|
1572
|
+
data,
|
|
1573
|
+
metadata: {
|
|
1574
|
+
...listingMetadata(identity),
|
|
1575
|
+
range: window ? null : ask.range,
|
|
1576
|
+
resolution: history.resolution,
|
|
1577
|
+
requestedRange: window ? null : ask.range,
|
|
1578
|
+
...(window ? { requestedWindow: window } : {}),
|
|
1579
|
+
currency: unit.currency,
|
|
1580
|
+
unit: unit.unit,
|
|
1581
|
+
interval,
|
|
1582
|
+
firstDate: data[0]?.date ?? null,
|
|
1583
|
+
lastDate: data.at(-1)?.date ?? null,
|
|
1584
|
+
// The first bar after the last gap or jump; the first bar when there is none.
|
|
1585
|
+
unbrokenFrom: historyUnbrokenFrom(data) ?? data[0]?.date ?? null,
|
|
1586
|
+
bars: data.length,
|
|
1587
|
+
maxDrawdown: drawdown,
|
|
1588
|
+
asOf: freshness?.asOf ?? null,
|
|
1589
|
+
...(volume ? { volumeUnit: volume.unit, volumePeriod: interval === "1d" ? "UTC day" : interval } : {}),
|
|
1590
|
+
},
|
|
1591
|
+
...(notes.length > 0 ? { warnings: notes } : {}),
|
|
1592
|
+
freshness,
|
|
1593
|
+
}, {
|
|
1594
|
+
heading: [listingHeading(identity), cliStyles.muted(historyFacts(unit, data).join(" · ")),
|
|
1595
|
+
...drawdown && interval ? [cliStyles.muted(maxDrawdownText(drawdown, { basis: `${BAR_INTERVAL_LABELS[interval]} closes`, price,
|
|
1596
|
+
date: (text) => intraday ? formatUtcTime(text) : text.slice(0, 10) }))] : []].join("\n"),
|
|
1597
|
+
dateKey: "date",
|
|
1598
|
+
textColumns: [...textColumns, HISTORY_FLAG_TEXT_COLUMN],
|
|
1599
|
+
// One header whatever the market is doing, for one symbol as for a list (HISTORY_EXPORT_COLUMNS).
|
|
1600
|
+
columns: HISTORY_EXPORT_COLUMNS,
|
|
1601
|
+
exportStatus: historyExportStatus,
|
|
1602
|
+
// Every exported row says what it is a bar of, the currency a price is in and its bar size, so it survives head and concatenation.
|
|
1603
|
+
exportIdentity: { symbolOrSeries: history.listing.key, currencyOrUnit: unit.currency ?? unit.unit, interval },
|
|
1604
|
+
});
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/** A symbol of a list: its bars, or why it has none. */
|
|
1609
|
+
type ListedHistory = { symbol: string; history: ListingHistory } | { symbol: string; error: string };
|
|
1610
|
+
|
|
1611
|
+
/**
|
|
1612
|
+
* `history --file holdings.txt` (or several symbols, or `-` for standard
|
|
1613
|
+
* input): every symbol's bars in one long table, `HISTORY_LIST_CONCURRENCY`
|
|
1614
|
+
* symbols loading at once. A symbol that serves nothing is one row with its
|
|
1615
|
+
* reason in `status`, and a warning; the command fails only when none served.
|
|
1616
|
+
*/
|
|
1617
|
+
async function runHistoryList(
|
|
1618
|
+
args: string[],
|
|
1619
|
+
file: string | undefined,
|
|
1620
|
+
exchange: string | undefined,
|
|
1621
|
+
ask: HistoryAsk,
|
|
1622
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
1623
|
+
) {
|
|
1624
|
+
const usage = `Usage: gloom ${HISTORY_USAGE}`;
|
|
1625
|
+
await withMarketData(ctx, async (market) => {
|
|
1626
|
+
const list = await readListedSymbols(args, file, market, ctx, usage);
|
|
1627
|
+
const refused = list.symbols.flatMap((symbol) => notASymbolError(symbol) ?? []);
|
|
1628
|
+
const symbols = list.symbols.filter((symbol) => !notASymbolError(symbol));
|
|
1629
|
+
if (symbols.length === 0) ctx.fail(refused.length === 1 ? refused[0]!.message : `No history to read: ${refusedList(refused)} are not symbols.`, usage);
|
|
1630
|
+
// A long list is a batch, not a lookup: it skips the per-symbol venue and moved-company lookups, as `quote` does.
|
|
1631
|
+
const lookups = symbols.length <= MAX_CHOICE_LOOKUPS;
|
|
1632
|
+
const listed = await mapWithConcurrency(symbols, HISTORY_LIST_CONCURRENCY, async (symbol): Promise<ListedHistory> => {
|
|
1633
|
+
try {
|
|
1634
|
+
const listing = await resolveCliListing(symbol, exchange, market, { ownExchangeWins: true });
|
|
1635
|
+
return { symbol, history: await loadListingHistory(listing, market, ctx, ask, { lookups }) };
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
const message = errorMessage(error) ?? `No history for ${symbol}`;
|
|
1638
|
+
return { symbol, error: isCliFailure(error) ? oneLineFailure(message, error.details) : message };
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
const served = listed.flatMap((entry) => "history" in entry && entry.history.data.length > 0 ? [entry.history] : []);
|
|
1642
|
+
// A symbol with no bars, whether its request failed or came back empty, is one row with the reason.
|
|
1643
|
+
const rows: HistoryListRow[] = listed.flatMap((entry) => "history" in entry
|
|
1644
|
+
? entry.history.data.length > 0
|
|
1645
|
+
? entry.history.data.map((row) => ({ symbol: entry.history.listing.key, ...row }))
|
|
1646
|
+
: [historyFailureRow(entry.history.listing.key, emptyHistoryNote(entry.history.listing.key, ask))]
|
|
1647
|
+
: [historyFailureRow(entry.symbol, entry.error)]);
|
|
1648
|
+
const bySymbol = new Map(served.map((history) => [history.listing.key, history] as const));
|
|
1649
|
+
const notes = [
|
|
1650
|
+
...list.notes,
|
|
1651
|
+
...refused.length > 0 ? [`Not ${refused.length === 1 ? "a symbol" : "symbols"}, no history read: ${refusedList(refused)}.`] : [],
|
|
1652
|
+
...symbolListNotes(listed.map((entry) => ({
|
|
1653
|
+
symbol: "history" in entry ? entry.history.listing.key : entry.symbol,
|
|
1654
|
+
notes: "history" in entry ? entry.history.notes : [entry.error],
|
|
1655
|
+
}))),
|
|
1656
|
+
];
|
|
1657
|
+
// The closing lines date the list by its newest bar; each row's as_of is its own symbol's.
|
|
1658
|
+
const lastBars = served.flatMap((history) => history.data.at(-1) ?? []);
|
|
1659
|
+
const pastWindow = ask.window && isPastPriceWindow(ask.window);
|
|
1660
|
+
const listFreshness = served.length === 0 ? undefined : rowsFreshness(lastBars, pastWindow && ask.window ? {
|
|
1661
|
+
status: "not-a-feed", basis: `price bars, ${priceWindowText(ask.window)}`, observedKey: "date", oldest: null,
|
|
1662
|
+
} : {
|
|
1663
|
+
...barHistoryFreshness(served.some((history) => history.intraday) ? null : served[0]!.interval ?? "1d"),
|
|
1664
|
+
observedKey: "date",
|
|
1665
|
+
oldest: null,
|
|
1666
|
+
});
|
|
1667
|
+
// A saved copy is marked on the bars a symbol loaded, not on the rows drawn from them (loadListingHistory
|
|
1668
|
+
// reads it there): the list ends "couldn't refresh; saved copy from <time>" for the oldest copy among its symbols.
|
|
1669
|
+
const savedAt = served.flatMap((history) => history.freshness?.savedAt ?? []).sort()[0];
|
|
1670
|
+
const freshness = listFreshness && savedAt && !listFreshness.savedAt ? { ...listFreshness, savedAt } : listFreshness;
|
|
1671
|
+
const decimals = (row: Record<string, unknown>) => bySymbol.get(String(row.symbol))?.decimals ?? 2;
|
|
1672
|
+
const intraday = served.some((history) => history.intraday);
|
|
1673
|
+
const textColumns = historyTextColumns(intraday, (value, row) => historyPrice(decimals(row))(value)).map((column) => column.key !== "volume" ? column
|
|
1674
|
+
: { ...column, format: historyVolumeCell((row) => {
|
|
1675
|
+
const history = bySymbol.get(String(row.symbol));
|
|
1676
|
+
return history && row.date && historyReportsVolume(history.data) ? MISSING_VOLUME_TEXT : "";
|
|
1677
|
+
}) });
|
|
1678
|
+
ctx.printResult({
|
|
1679
|
+
data: rows,
|
|
1680
|
+
metadata: {
|
|
1681
|
+
range: ask.window ? null : ask.range,
|
|
1682
|
+
requestedRange: ask.window ? null : ask.range,
|
|
1683
|
+
...(ask.window ? { requestedWindow: ask.window } : {}),
|
|
1684
|
+
// Each symbol as a single `history` describes it, or the reason it has no bars.
|
|
1685
|
+
symbols: listed.map((entry) => "history" in entry ? {
|
|
1686
|
+
...listingMetadata(entry.history.identity),
|
|
1687
|
+
currency: entry.history.unit.currency,
|
|
1688
|
+
unit: entry.history.unit.unit,
|
|
1689
|
+
interval: entry.history.interval,
|
|
1690
|
+
firstDate: entry.history.data[0]?.date ?? null,
|
|
1691
|
+
lastDate: entry.history.data.at(-1)?.date ?? null,
|
|
1692
|
+
bars: entry.history.data.length,
|
|
1693
|
+
asOf: entry.history.freshness?.asOf ?? null,
|
|
1694
|
+
} : { symbol: entry.symbol, error: entry.error }),
|
|
1695
|
+
},
|
|
1696
|
+
...(notes.length > 0 ? { warnings: notes } : {}),
|
|
1697
|
+
...(served.length === 0 ? { failure: { code: "cli_error", message: `No history for ${symbols.join(", ")}.` } } : {}),
|
|
1698
|
+
freshness,
|
|
1699
|
+
// With no bars to date, an export still ends its rows with what each is of, under the same header.
|
|
1700
|
+
...(freshness ? {} : { exportFreshness: rowsFreshness([], { status: "not-a-feed", basis: "price bars" }) }),
|
|
1701
|
+
}, {
|
|
1702
|
+
seriesKey: "symbol",
|
|
1703
|
+
dateKey: "date",
|
|
1704
|
+
textColumns: [{ key: "symbol", header: "Symbol", shrink: false }, ...textColumns,
|
|
1705
|
+
{ key: "currency", header: "Cur", shrink: false, format: currencyCell }, HISTORY_FLAG_TEXT_COLUMN],
|
|
1706
|
+
columns: HISTORY_EXPORT_COLUMNS,
|
|
1707
|
+
exportStatus: historyExportStatus,
|
|
1708
|
+
// Every row names its symbol, the currency its prices are in, its bar size and the as-of of its own symbol's bars.
|
|
1709
|
+
exportIdentity: (row) => {
|
|
1710
|
+
const history = bySymbol.get(String(row.symbol));
|
|
1711
|
+
return {
|
|
1712
|
+
symbolOrSeries: String(row.symbol),
|
|
1713
|
+
currencyOrUnit: history ? history.unit.currency ?? history.unit.unit : null,
|
|
1714
|
+
interval: history?.interval ?? null,
|
|
1715
|
+
// A symbol with no bars has no as-of of its own.
|
|
1716
|
+
asOf: history ? history.freshness?.asOf ?? undefined : null,
|
|
1717
|
+
};
|
|
1718
|
+
},
|
|
1719
|
+
});
|
|
1720
|
+
// The table and the warnings name each symbol with no bars; with none served, the exit status says so too.
|
|
1721
|
+
if (served.length === 0) process.exitCode = CLI_EXIT_CODES.error;
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
type FinancialsView = "statements" | "fundamentals" | "valuation";
|
|
1726
|
+
|
|
1727
|
+
const STATEMENT_COLUMNS: CliResultColumn<ReturnType<typeof financialStatementRows>[number]>[] = [
|
|
1728
|
+
{ key: "date", header: "Date" },
|
|
1729
|
+
{ key: "revenue", header: "Revenue", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
|
|
1730
|
+
{ key: "grossProfit", header: "Gross", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
|
|
1731
|
+
{ key: "operatingIncome", header: "Op Inc", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
|
|
1732
|
+
{ key: "netIncome", header: "Net Inc", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
|
|
1733
|
+
{ key: "eps", header: "EPS", align: "right", format: (value) => value == null ? "" : formatPerShareNumber(Number(value)) },
|
|
1734
|
+
{ key: "currency", header: "Cur" },
|
|
1735
|
+
];
|
|
1736
|
+
|
|
1737
|
+
/** A symbol as typed in capitals (`NEE`, `BP:LSE`, `7203.T`), never a word of a company's name. */
|
|
1738
|
+
const TYPED_SYMBOL = /^[A-Z0-9][A-Z0-9.:^=&-]{0,15}$/;
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* The symbols of a command given several where it takes one: `NEE FSLR` as
|
|
1742
|
+
* two words, or as one (`"NEE FSLR"`, `NEE,FSLR`). One word with spaces is
|
|
1743
|
+
* a list only when every part is a symbol as typed, so a company's name
|
|
1744
|
+
* (`"Bank of America"`) is still searched for. Null for one symbol.
|
|
1745
|
+
*/
|
|
1746
|
+
export function comparedSymbols(args: readonly string[]): string[] | null {
|
|
1747
|
+
const words = args.flatMap((arg) => arg.split(/[\s,]+/)).filter(Boolean);
|
|
1748
|
+
if (words.length < 2) return null;
|
|
1749
|
+
if (args.length > 1 || args.some((arg) => arg.includes(","))) return words;
|
|
1750
|
+
return words.every((word) => TYPED_SYMBOL.test(word)) ? words : null;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1], view: FinancialsView) {
|
|
1754
|
+
const args = [...rawArgs];
|
|
1755
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
1756
|
+
const sectionFlag = args.some((arg) => arg === "--section" || arg.startsWith("--section="));
|
|
1757
|
+
const section = takeOption(args, "--section");
|
|
1758
|
+
const commandName = view === "statements" ? "financials" : view;
|
|
1759
|
+
// `fundamentals --tax-rate 40.8 [--state-rate 9.3 --state CA]`: a muni fund's tax-equivalent yield and muni/Treasury ratio.
|
|
1760
|
+
let rates: TaxRates | null = null;
|
|
1761
|
+
if (view === "fundamentals") {
|
|
1762
|
+
const taken = (flag: string) => {
|
|
1763
|
+
const given = args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
1764
|
+
const value = takeOption(args, flag);
|
|
1765
|
+
if (given && !value?.trim()) ctx.fail(`${flag} needs a value, such as --tax-rate 40.8 --state-rate 9.3 --state CA.`);
|
|
1766
|
+
return value;
|
|
1767
|
+
};
|
|
1768
|
+
try {
|
|
1769
|
+
rates = parseTaxRates({ taxRate: taken("--tax-rate"), stateRate: taken("--state-rate"), state: taken("--state") });
|
|
1770
|
+
} catch (error) {
|
|
1771
|
+
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
const tabular = ctx.cliOptions.format === "csv" || ctx.cliOptions.format === "ndjson";
|
|
1775
|
+
if (sectionFlag && !tabular) ctx.fail("--section picks one table of --csv or --ndjson output.");
|
|
1776
|
+
if (sectionFlag && !section?.trim()) ctx.fail("--section needs a section title or number.");
|
|
1777
|
+
// Several symbols are a comparison, which RV draws side by side.
|
|
1778
|
+
const compared = view === "valuation" ? comparedSymbols(args) : null;
|
|
1779
|
+
if (compared) {
|
|
1780
|
+
ctx.fail(`valuation takes one symbol; got ${compared.join(" ")}.`,
|
|
1781
|
+
`To compare their valuations side by side: gloom fn RV ${compared.join(",")}\nUsage: gloom valuation <symbol>`);
|
|
1782
|
+
}
|
|
1783
|
+
const raw = requireOneArg(args, `${commandName} <symbol>`, "symbol", ctx);
|
|
1784
|
+
const selectTables = (tables: CliReportTables) => {
|
|
1785
|
+
try {
|
|
1786
|
+
return selectReportTables(tables, section);
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
return ctx.fail(error instanceof Error ? error.message : String(error));
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
await withMarketData(ctx, async (market) => {
|
|
1792
|
+
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
1793
|
+
const venues = loadBareVenues(listing, market);
|
|
1794
|
+
const loaded = await loadForListing(listing, market, ctx, () => market.dataProvider.getTickerFinancials(
|
|
1795
|
+
listing.request.symbol, listing.request.exchange, { cacheMode: ctx.cliOptions.refresh ? "refresh" : "default" },
|
|
1796
|
+
), undefined, { command: commandName, noun: commandName === "valuation" ? "valuation data" : commandName });
|
|
1797
|
+
// A converted TTM block at the rate `fx` reports, as `ticker` shows it; only the US dollar legs are read.
|
|
1798
|
+
const financials = view === "statements" ? loaded
|
|
1799
|
+
: await atReportTtmRate(loaded, createBaseConverter(market.dataProvider, "USD"));
|
|
1800
|
+
// A trailing yield is measured from the cash history as DVD measures it, a forward yield dated ahead names the
|
|
1801
|
+
// declared distribution it annualizes (not in JSON), and one whose dividend stopped says so; read beside the listing notes.
|
|
1802
|
+
const distributions = view === "statements" ? null : loadDividendDistributions(
|
|
1803
|
+
market.dataProvider, listing.request, financials, { declared: ctx.cliOptions.format !== "json" });
|
|
1804
|
+
// A fund's facts: its bond figures beside its distribution yield, and whether fn ETF has any to point to.
|
|
1805
|
+
const fundFacts = view === "statements" ? null : loadReportFundFacts(listing.request.symbol,
|
|
1806
|
+
financials.quote?.tradingExchangeName || listing.request.exchange || financials.quote?.listingExchangeName || financials.quote?.exchangeName,
|
|
1807
|
+
financials, listing.saved);
|
|
1808
|
+
const treasury = rates ? loadTreasuryTenors() : null;
|
|
1809
|
+
const identity = listingIdentity(listing, financials.quote);
|
|
1810
|
+
const choice = await listingChoiceNotes(listing, market, financials.quote, venues);
|
|
1811
|
+
const data: FinancialsCliData = {
|
|
1812
|
+
symbol: listing.key,
|
|
1813
|
+
exchange: identity.exchange,
|
|
1814
|
+
providerId: financials.quote?.providerId ?? null,
|
|
1815
|
+
...financials,
|
|
1816
|
+
};
|
|
1817
|
+
if (view !== "statements") {
|
|
1818
|
+
// A valuation is measured at the quote's regular session price, so the quote dates it.
|
|
1819
|
+
const freshness = view === "valuation" ? valuationFreshness(financials) : fundamentalsFreshness(financials);
|
|
1820
|
+
// Market cap, the multiples and the yield at the regular session price, as the text gives them; the quote stays as sent.
|
|
1821
|
+
const priced = atRegularSessionPrice(financials);
|
|
1822
|
+
// One market cap per view: the share count times the price where the provider's sits more than 2% from it.
|
|
1823
|
+
const capitalization = viewMarketCapitalization(priced.quote, priced.fundamentals, regularSessionValuationPrice(priced.quote));
|
|
1824
|
+
// The filings behind the latest statements, and for a bank its deposits, uninsured share and CRE.
|
|
1825
|
+
const [statementFilings, bank] = view === "fundamentals"
|
|
1826
|
+
? await Promise.all([
|
|
1827
|
+
loadStatementFilings(market.statementSources, listing.request, {
|
|
1828
|
+
quarter: latestFinancialPeriod(financials.quarterlyStatements, (row) => row.date),
|
|
1829
|
+
annual: latestFinancialPeriod(financials.annualStatements, (row) => row.date),
|
|
1830
|
+
}),
|
|
1831
|
+
loadBankFigures(market.statementSources, listing.request, financials),
|
|
1832
|
+
])
|
|
1833
|
+
: [null, { bank: null, note: null }];
|
|
1834
|
+
const facts = await fundFacts;
|
|
1835
|
+
const bond = facts?.bond ?? null;
|
|
1836
|
+
// On the SEC yield when the sponsor publishes one, else the distribution yield the line above shows.
|
|
1837
|
+
const taxEquivalent = rates ? analyzeTaxEquivalent({
|
|
1838
|
+
symbol: identity.symbol || listing.key, bond, rates, treasury: await treasury,
|
|
1839
|
+
distribution: distributionBasis(priced.fundamentals?.dividendYield, priced.fundamentals?.dividendYieldBasis === "trailing" ? "trailing" : "forward"),
|
|
1840
|
+
}) : null;
|
|
1841
|
+
const report = { ...data, distributions: await distributions, statementFilings, bank: bank.bank, bankNote: bank.note, bondFund: bond, fundFacts: facts, taxEquivalent,
|
|
1842
|
+
// A base currency other than USD reads every amount with its code, as `ticker` does.
|
|
1843
|
+
currencyCodes: writesCurrencyCodes(market.config) };
|
|
1844
|
+
ctx.printResult({
|
|
1845
|
+
// JSON reads a multiple over a loss as null with its reason, never as a number.
|
|
1846
|
+
data: {
|
|
1847
|
+
...data,
|
|
1848
|
+
...view === "valuation" ? {
|
|
1849
|
+
...getQuoteSessionFields(financials.quote),
|
|
1850
|
+
marketCap: capitalization?.value ?? null,
|
|
1851
|
+
marketCapCurrency: capitalization?.currency ?? null,
|
|
1852
|
+
} : {},
|
|
1853
|
+
fundamentals: priced.fundamentals && exportedDividendFundamentals(exportedFundamentals(priced.fundamentals)!, report.distributions),
|
|
1854
|
+
// A bond fund's sponsor figures; fundamentals.dividendYield is then its distribution yield, not its SEC yield.
|
|
1855
|
+
...bond ? { bondFund: bond } : {},
|
|
1856
|
+
...view === "fundamentals" ? { statementFilings, bank: bank.bank, ...bank.note ? { bankNote: bank.note } : {} } : {},
|
|
1857
|
+
...taxEquivalent ? { taxEquivalent: taxEquivalentJson(taxEquivalent) } : {},
|
|
1858
|
+
},
|
|
1859
|
+
freshness,
|
|
1860
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
1861
|
+
}, {
|
|
1862
|
+
text: () => renderFundamentalsReport(report, view),
|
|
1863
|
+
...(tabular ? { tables: selectTables(fundamentalsReportTables(report, view, freshness)) } : {}),
|
|
1864
|
+
exportIdentity: { symbolOrSeries: listing.key },
|
|
1865
|
+
});
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
const rows = financialStatementRows(data);
|
|
1869
|
+
const freshness = rowsFreshness(rows, {
|
|
1870
|
+
...REPORTED_DATA, basis: "financial statements", observedKey: "date", oldest: null,
|
|
1871
|
+
});
|
|
1872
|
+
// The rows are the annual years served; older ones are not, and the closing lines say so, after how old
|
|
1873
|
+
// the latest year is when it ended over 15 months ago.
|
|
1874
|
+
const stale = stalePeriodNote(rows.map((row) => ({ date: row.date, lead: row.revenue != null })));
|
|
1875
|
+
// Years filled from the listing's exchange, for the revenue and net income columns.
|
|
1876
|
+
const filled = filledPeriodsNote(financials.annualStatements.filter((statement) => rows.some((row) => row.date === statement.date)),
|
|
1877
|
+
new Set(["totalRevenue", "netIncome"]));
|
|
1878
|
+
const historyNotes = [...stale ? [stale] : [], ...filled ? [filled] : [], ...statementHistoryNotes("annual", rows.map((row) => row.date), listing.key)];
|
|
1879
|
+
// NDJSON keeps the raw statement rows it always wrote; CSV gets the table and its closing lines.
|
|
1880
|
+
const tables = tabular
|
|
1881
|
+
? selectTables({
|
|
1882
|
+
tables: [exportRowsTable("Annual statements", STATEMENT_COLUMNS, rows)],
|
|
1883
|
+
footer: reportFooterLines({ freshness, notes: historyNotes }),
|
|
1884
|
+
})
|
|
1885
|
+
: undefined;
|
|
1886
|
+
ctx.printResult({
|
|
1887
|
+
data,
|
|
1888
|
+
metadata: {
|
|
1889
|
+
...listingMetadata(identity),
|
|
1890
|
+
providerId: financials.quote?.providerId,
|
|
1891
|
+
annualStatements: financials.annualStatements.length,
|
|
1892
|
+
quarterlyStatements: financials.quarterlyStatements.length,
|
|
1893
|
+
fundamentals: exportedFundamentals(financials.fundamentals),
|
|
1894
|
+
profile: financials.profile,
|
|
1895
|
+
...(historyNotes.length > 0 ? { notes: historyNotes } : {}),
|
|
1896
|
+
},
|
|
1897
|
+
freshness,
|
|
1898
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
1899
|
+
}, {
|
|
1900
|
+
heading: listingHeading(identity),
|
|
1901
|
+
rows: () => rows,
|
|
1902
|
+
columns: STATEMENT_COLUMNS,
|
|
1903
|
+
...(historyNotes.length > 0 ? { footnote: historyNotes.join("\n"), exportNotes: historyNotes } : {}),
|
|
1904
|
+
...(tables && ctx.cliOptions.format === "csv" ? { tables } : {}),
|
|
1905
|
+
// Annual statements in the company's reporting currency.
|
|
1906
|
+
exportIdentity: { symbolOrSeries: listing.key, currencyOrUnit: rows.find((row) => row.currency)?.currency ?? null, interval: "annual" },
|
|
1907
|
+
});
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
const NEWS_COLUMNS = [
|
|
1912
|
+
// UTC with the zone named, as every CLI time prints, rather than the host zone unlabeled.
|
|
1913
|
+
{
|
|
1914
|
+
key: "publishedAt",
|
|
1915
|
+
header: "Published",
|
|
1916
|
+
format: (value: unknown) => typeof value === "string" ? formatUtcTime(value) : "",
|
|
1917
|
+
},
|
|
1918
|
+
{ key: "source", header: "Source" },
|
|
1919
|
+
{ key: "title", header: "Title" },
|
|
1920
|
+
{ key: "tickers", header: "Tickers" },
|
|
1921
|
+
{ key: "url", header: "URL", optional: true },
|
|
1922
|
+
];
|
|
1923
|
+
|
|
1924
|
+
/**
|
|
1925
|
+
* Stories matching text, printed as a symbol's news is, under a line saying
|
|
1926
|
+
* what they match. A text naming a commodity that matched nothing says so and
|
|
1927
|
+
* offers its future's news when that has stories; `commodity: false` for the
|
|
1928
|
+
* future's own news, which is that route. A search that failed (`failure`,
|
|
1929
|
+
* its message) says so, with `empty`, the lookup's own answer, rather than
|
|
1930
|
+
* that no story matches.
|
|
1931
|
+
*/
|
|
1932
|
+
async function printNewsSearch(
|
|
1933
|
+
text: string,
|
|
1934
|
+
articles: NewsArticle[],
|
|
1935
|
+
{ reason, empty, commodity = true, failure, ticker = null }: {
|
|
1936
|
+
reason?: string;
|
|
1937
|
+
empty?: string;
|
|
1938
|
+
commodity?: boolean;
|
|
1939
|
+
failure?: string;
|
|
1940
|
+
/** The listing a coin pair's search stands in for. */
|
|
1941
|
+
ticker?: string | null;
|
|
1942
|
+
},
|
|
1943
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
1944
|
+
) {
|
|
1945
|
+
const searched = failure === undefined;
|
|
1946
|
+
const named = searched && commodity && articles.length === 0 && commodityNamedIn(text) !== null;
|
|
1947
|
+
const route = named ? await commodityNewsRoute(text) : null;
|
|
1948
|
+
ctx.printResult({
|
|
1949
|
+
data: articles,
|
|
1950
|
+
...(searched ? {} : { warnings: [`Searching stories for "${text}" failed: ${failure}`] }),
|
|
1951
|
+
metadata: { ticker, search: text, feed: null, ...(route ? { futuresNews: route.command } : {}) },
|
|
1952
|
+
freshness: rowsFreshness(newsRows(articles), cloudNewsFreshness(await cloudRealtimeAccess())),
|
|
1953
|
+
}, {
|
|
1954
|
+
heading: newsSearchHeading(text, reason),
|
|
1955
|
+
rows: newsRows,
|
|
1956
|
+
columns: NEWS_COLUMNS,
|
|
1957
|
+
empty: named ? noStoriesMessage(text, route) : empty ?? (searched ? noStoriesMessage(text, null) : "No stories to show."),
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/** The ticker a metal's name is in capitals, when one answers to exactly that: GOLD, Gold.com Inc., for "gold". */
|
|
1962
|
+
async function tickerOfWord(word: string, market: MarketContext): Promise<{ symbol: string; name: string | null } | null> {
|
|
1963
|
+
const symbol = word.toUpperCase();
|
|
1964
|
+
const listing = await resolveCliListing(symbol, null, market).catch(() => {
|
|
1965
|
+
// Intentional ignore: a word no listing answers to offers no ticker under the metal's news.
|
|
1966
|
+
return null;
|
|
1967
|
+
});
|
|
1968
|
+
if (!listing || listing.symbol.toUpperCase() !== symbol) return null;
|
|
1969
|
+
const quote = await loadListingQuote(market.dataProvider, listing);
|
|
1970
|
+
const identity = listingIdentity(listing, quote);
|
|
1971
|
+
return quote !== null || identity.name != null ? { symbol: listing.symbol, name: identity.name ?? null } : null;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
/**
|
|
1975
|
+
* A metal's market news: the stories tagged METALS that mention
|
|
1976
|
+
* it, for its future, its board code or its name. Under them, the ticker its
|
|
1977
|
+
* name is in capitals, whose company news `news GOLD` keeps.
|
|
1978
|
+
*/
|
|
1979
|
+
async function printMetalNews(route: MetalNewsRoute, limit: number, ctx: Parameters<CliCommandDef["execute"]>[1], market?: MarketContext) {
|
|
1980
|
+
const search = searchNewsStories(route.metal, limit, [METALS_NEWS_TOPIC]).then((found) => ({ found, failure: undefined }), (error: unknown) => {
|
|
1981
|
+
logFailure("cli.commands-market", "Search metals news failed")(error);
|
|
1982
|
+
return { found: [] as NewsArticle[], failure: errorMessage(error) ?? "no answer" };
|
|
1983
|
+
});
|
|
1984
|
+
const word = route.word;
|
|
1985
|
+
const ticker = word
|
|
1986
|
+
? await (market ? tickerOfWord(word, market) : withMarketData(ctx, (context) => tickerOfWord(word, context)))
|
|
1987
|
+
.catch(logFailure("cli.commands-market", "Read the ticker a metal's name is failed", null))
|
|
1988
|
+
: null;
|
|
1989
|
+
const { found, failure } = await search;
|
|
1990
|
+
const companyNews = ticker ? `gloom news ${ticker.symbol}` : null;
|
|
1991
|
+
ctx.printResult({
|
|
1992
|
+
data: found,
|
|
1993
|
+
...(failure === undefined ? {} : { warnings: [`Searching metals stories for "${route.metal}" failed: ${failure}`] }),
|
|
1994
|
+
metadata: { ticker: null, search: route.metal, topics: [METALS_NEWS_TOPIC], feed: null, ...(companyNews ? { companyNews } : {}) },
|
|
1995
|
+
freshness: rowsFreshness(newsRows(found), cloudNewsFreshness(await cloudRealtimeAccess())),
|
|
1996
|
+
}, {
|
|
1997
|
+
heading: metalNewsHeading(route),
|
|
1998
|
+
rows: newsRows,
|
|
1999
|
+
columns: NEWS_COLUMNS,
|
|
2000
|
+
empty: failure === undefined ? noMetalStoriesMessage(route) : "No stories to show.",
|
|
2001
|
+
...(ticker ? { footnote: `${word} ${cliArrow("→")} ${route.metal}'s market news. For ${ticker.name ?? ticker.symbol} (ticker ${ticker.symbol}), try: ${companyNews}` } : {}),
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
2006
|
+
const args = [...rawArgs];
|
|
2007
|
+
const feed = parseNewsFeed(takeOption(args, "--feed"));
|
|
2008
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
2009
|
+
// Stories come newest first, so the newest n are the first n either way.
|
|
2010
|
+
const limit = ctx.cliOptions.tail ?? ctx.cliOptions.limit ?? 20;
|
|
2011
|
+
const text = newsSearchText(args);
|
|
2012
|
+
if (text != null) {
|
|
2013
|
+
if (exchangeOption || feed) ctx.fail(`${exchangeOption ? "--exchange" : "--feed"} goes with a symbol, not with text to search stories for.`, "Usage: gloom news <query>");
|
|
2014
|
+
const problem = newsSearchTextProblem(text);
|
|
2015
|
+
if (problem) ctx.fail(problem);
|
|
2016
|
+
await printNewsSearch(text, await searchNewsStories(text, limit), {}, ctx);
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
// A metal's future or name is its market's news, not a company's: GOLD in capitals stays Gold.com's.
|
|
2020
|
+
const metal = args.length === 1 && !exchangeOption && !feed ? metalNewsRoute(args[0]!) : null;
|
|
2021
|
+
if (metal) {
|
|
2022
|
+
await printMetalNews(metal, limit, ctx);
|
|
2023
|
+
return;
|
|
2024
|
+
}
|
|
2025
|
+
if (exchangeOption && !args[0]) ctx.fail("--exchange needs a symbol: gloom news <symbol> --exchange <code>");
|
|
2026
|
+
await withMarketData(ctx, async (market) => {
|
|
2027
|
+
const listing = args[0] ? await requireCliListing(args[0], exchangeOption, market, ctx) : null;
|
|
2028
|
+
const quoteLoad = listing ? loadListingQuote(market.dataProvider, listing) : Promise.resolve(null);
|
|
2029
|
+
// A coin pair's feed searches for the coin when no story is filed under the pair, by the name its quote gives.
|
|
2030
|
+
const coin = !!listing && !feed && isRoundTheClockCoin(listing.request.symbol, listing.request.exchange);
|
|
2031
|
+
const loadNews = async () => market.dataProvider.getNews({
|
|
2032
|
+
feed: feed ?? (listing ? "ticker" : "latest"),
|
|
2033
|
+
// Still set for news plugins that read the deprecated scope.
|
|
2034
|
+
scope: listing ? "ticker" : "global",
|
|
2035
|
+
ticker: listing?.request.symbol,
|
|
2036
|
+
exchange: listing?.request.exchange || undefined,
|
|
2037
|
+
limit,
|
|
2038
|
+
...(coin ? { listingName: (await quoteLoad)?.name || listing!.saved?.metadata.name || undefined } : {}),
|
|
2039
|
+
});
|
|
2040
|
+
const [articles, quote] = await Promise.all([
|
|
2041
|
+
listing ? loadForListing(listing, market, ctx, loadNews, (found) => found.length === 0) : loadNews(),
|
|
2042
|
+
quoteLoad,
|
|
2043
|
+
]);
|
|
2044
|
+
const searchedFor = listing ? articles[0]?.searchedFor : undefined;
|
|
2045
|
+
if (listing && searchedFor) {
|
|
2046
|
+
await printNewsSearch(searchedFor, articles, { reason: `no stories filed under ${listing.symbol}`, ticker: listing.key }, ctx);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
const identity = listing ? listingIdentity(listing, quote) : null;
|
|
2050
|
+
const resolved = quote !== null || identity?.name != null;
|
|
2051
|
+
const unfiled = !!listing && !listing.exchange && !feed && articles.length === 0;
|
|
2052
|
+
// A future's news is its commodity's: news is filed under company tickers, so KC=F (or a bare code
|
|
2053
|
+
// no security keeps, CT) searches the stories of coffee, which the service matches to KC=F's links too.
|
|
2054
|
+
const future = unfiled && isFuturesInstrument(listing!.symbol, quote?.instrumentType)
|
|
2055
|
+
? futuresBoardContract(listing!.symbol) ?? bareFuturesCode(args[0]) : null;
|
|
2056
|
+
const commodity = future ? commoditySearchName(future) : null;
|
|
2057
|
+
// A bare word no listing answers to is text, and so is a futures board code (TTF), which resolves
|
|
2058
|
+
// to its future: the stories that mention the word are the news.
|
|
2059
|
+
const word = commodity ?? (unfiled ? newsFallbackText(args[0]!) : null);
|
|
2060
|
+
const futuresCode = !!word && !!bareFuturesCode(word) && isFuturesInstrument(listing!.symbol, quote?.instrumentType);
|
|
2061
|
+
const reason = commodity ? `the commodity ${future!.symbol} trades`
|
|
2062
|
+
: !resolved ? "no ticker by that name" : futuresCode ? "a futures code, not a ticker" : null;
|
|
2063
|
+
const emptyMessage = (searched: boolean) => listing && identity
|
|
2064
|
+
? newsEmptyMessage(searched && word ? word : listing.symbol, listingTitle(identity), quote, resolved, searched)
|
|
2065
|
+
: undefined;
|
|
2066
|
+
// A metal's board code no listing answers to (GC, SI) is the metal's market news.
|
|
2067
|
+
const metalCode = !resolved && unfiled && !exchangeOption ? metalCodeNewsRoute(args[0]!) : null;
|
|
2068
|
+
if (metalCode) {
|
|
2069
|
+
await printMetalNews(metalCode, limit, ctx, market);
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
if (word && reason) {
|
|
2073
|
+
// The lookup that found no ticker already answered; a search that fails leaves that answer.
|
|
2074
|
+
const search = await searchNewsStories(word, limit).then((found) => ({ found, failure: undefined }), (error: unknown) => {
|
|
2075
|
+
logFailure("cli.commands-market", "Search news stories failed")(error);
|
|
2076
|
+
return { found: [], failure: errorMessage(error) ?? "no answer" };
|
|
2077
|
+
});
|
|
2078
|
+
const searched = search.failure === undefined;
|
|
2079
|
+
await printNewsSearch(word, search.found, { reason, empty: emptyMessage(searched && !commodity), commodity: !commodity, failure: search.failure }, ctx);
|
|
2080
|
+
return;
|
|
2081
|
+
}
|
|
2082
|
+
const empty = emptyMessage(false);
|
|
2083
|
+
// A bare coin ticker read as another listing (ETH, the Grayscale fund) says where the coin is, as `quote` does.
|
|
2084
|
+
const read = listing ? [...listingReadNotes(listing), ...coinListingNotes(listing, quote)] : [];
|
|
2085
|
+
// A word that is also the company's name ("gold" for Gold.com Inc.) says once which ticker it found and how
|
|
2086
|
+
// to search the stories that mention it instead, when those stories are mostly about something else.
|
|
2087
|
+
const typed = args[0]?.trim() ?? "";
|
|
2088
|
+
const wordHint = listing && resolved && !listing.exchange && !feed && !exchangeOption && commonWordCandidate(typed, identity?.name)
|
|
2089
|
+
&& wordMeansMore(await searchNewsStories(typed, 20).catch(logFailure("cli.commands-market", "Search news stories for a ticker word failed", [])), listing.symbol)
|
|
2090
|
+
? `${typed} ${cliArrow("→")} ${identity?.name ?? listing.symbol} (ticker ${listing.symbol}). For stories that mention ${typed.toLowerCase()}, try: ${quotedSearchCommand(typed.toLowerCase())}`
|
|
2091
|
+
: null;
|
|
2092
|
+
ctx.printResult({
|
|
2093
|
+
data: articles,
|
|
2094
|
+
...(read.length > 0 ? { warnings: read } : {}),
|
|
2095
|
+
metadata: { ticker: listing?.key ?? null, ...(identity ? listingMetadata(identity) : {}), feed: feed ?? null, ...(wordHint ? { textSearch: quotedSearchCommand(typed.toLowerCase()) } : {}) },
|
|
2096
|
+
freshness: rowsFreshness(newsRows(articles), cloudNewsFreshness(await cloudRealtimeAccess())),
|
|
2097
|
+
}, {
|
|
2098
|
+
...(identity ? { heading: listingHeading(identity) } : {}),
|
|
2099
|
+
rows: newsRows,
|
|
2100
|
+
columns: NEWS_COLUMNS,
|
|
2101
|
+
...(empty ? { empty } : {}),
|
|
2102
|
+
...(wordHint ? { footnote: wordHint } : {}),
|
|
2103
|
+
});
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
// Exports keep the columns they had and add the items after them: codes, then their names.
|
|
2108
|
+
const FILING_COLUMNS = [
|
|
2109
|
+
{ key: "filingDate", header: "Date" },
|
|
2110
|
+
{ key: "form", header: "Form" },
|
|
2111
|
+
{ key: "companyName", header: "Company", maxWidth: 24 },
|
|
2112
|
+
{ key: "url", header: "URL", optional: true },
|
|
2113
|
+
{ key: "items", header: "Items" },
|
|
2114
|
+
{ key: "itemsLabel", header: "Item names" },
|
|
2115
|
+
];
|
|
2116
|
+
|
|
2117
|
+
// The text table names the items before the URL, which a narrow terminal drops first.
|
|
2118
|
+
const FILING_TEXT_COLUMNS = [
|
|
2119
|
+
{ key: "filingDate", header: "Date" },
|
|
2120
|
+
{ key: "form", header: "Form" },
|
|
2121
|
+
{ key: "companyName", header: "Company", maxWidth: 24 },
|
|
2122
|
+
{ key: "itemsLabel", header: "Items" },
|
|
2123
|
+
{ key: "url", header: "URL", optional: true },
|
|
2124
|
+
];
|
|
2125
|
+
|
|
2126
|
+
/** The US listing the SEC files a symbol under, from the symbol's venues: NYSE for SAN's Banco Santander. */
|
|
2127
|
+
async function registrantUsExchange(market: MarketContext, symbol: string, registrantName: string): Promise<string | null> {
|
|
2128
|
+
const venues = (await listingVenues(symbol, market).catch(logFailure("cli.commands-market", "Read listing venues for filings failed", [])))
|
|
2129
|
+
.filter((venue) => !nonUsSecListingVenue(symbol, venue.exchange) && !areDifferentCompanies(venue.name, registrantName));
|
|
2130
|
+
return (venues.find((venue) => isUsListingExchange(venue.exchange)) ?? venues[0])?.exchange ?? null;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
/** ASX announcements as `filings` lists them for a listing that files nothing with the SEC. */
|
|
2134
|
+
const ANNOUNCEMENT_COLUMNS = [
|
|
2135
|
+
{ key: "date", header: "Released", format: (value: unknown) => typeof value === "string" ? `${value.slice(0, 16).replace("T", " ")} UTC` : "" },
|
|
2136
|
+
{ key: "headline", header: "Headline", maxWidth: 60 },
|
|
2137
|
+
{ key: "type", header: "Type", maxWidth: 36 },
|
|
2138
|
+
{ key: "priceSensitive", header: "Price sensitive", format: (value: unknown) => value === true ? "yes" : "" },
|
|
2139
|
+
{ key: "url", header: "URL", optional: true },
|
|
2140
|
+
];
|
|
2141
|
+
|
|
2142
|
+
const ASX_ANNOUNCEMENTS = { source: "ASX company announcements", status: "not-a-feed", basis: "exchange announcements", oldest: null } as const;
|
|
2143
|
+
|
|
2144
|
+
function announcementRows(payload: ExchangeAnnouncementsPayload) {
|
|
2145
|
+
return payload.announcements.map((item) => ({
|
|
2146
|
+
date: item.date,
|
|
2147
|
+
headline: item.headline,
|
|
2148
|
+
type: item.types.join("; "),
|
|
2149
|
+
priceSensitive: item.priceSensitive,
|
|
2150
|
+
url: item.url,
|
|
2151
|
+
}));
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
2155
|
+
const args = [...rawArgs];
|
|
2156
|
+
// Filings come newest first, so --tail asks for the same first n as --limit.
|
|
2157
|
+
const count = parsePositiveInt(takeOption(args, "--count"), ctx.cliOptions.tail ?? ctx.cliOptions.limit ?? 15, "Count", ctx);
|
|
2158
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
2159
|
+
const form = takeOption(args, "--form")?.trim() || null;
|
|
2160
|
+
const raw = requireOneArg(args, "filings <symbol> [--count <n>] [--form <form>]", "symbol", ctx);
|
|
2161
|
+
await withMarketData(ctx, async (market) => {
|
|
2162
|
+
// NST.AX is NST:ASX, as quote, valuation and the pane functions read it: the SEC is asked by venue.
|
|
2163
|
+
const listing = await requireCliListing(raw, exchangeOption, market, ctx, { venueSuffix: true });
|
|
2164
|
+
const venues = loadBareVenues(listing, market);
|
|
2165
|
+
const { symbol, exchange } = listing.request;
|
|
2166
|
+
// A listing outside the US sends its company to the lookup, which needs its quote first.
|
|
2167
|
+
const quotePromise = loadListingQuote(market.dataProvider, listing);
|
|
2168
|
+
const identity = nonUsSecListingVenue(symbol, exchange) ? listingIdentity(listing, await quotePromise) : null;
|
|
2169
|
+
// Where a listing abroad publishes its announcements, whatever the SEC holds for it.
|
|
2170
|
+
const page = identity ? exchangeAnnouncementsPage(identity.symbol, identity.exchange) : null;
|
|
2171
|
+
const pageLine = page ? `${page.name}: ${page.url}` : null;
|
|
2172
|
+
try {
|
|
2173
|
+
const context = identity?.name ? { listingName: identity.name } : undefined;
|
|
2174
|
+
const [filings, quote] = await Promise.all([
|
|
2175
|
+
loadForListing(listing, market, ctx, () => form
|
|
2176
|
+
// A form filter searches every filing the service lists for the issuer, as the SEC pane does.
|
|
2177
|
+
? market.dataProvider.getSecFilings(symbol, SEC_FILING_FETCH_LIMIT, exchange, context)
|
|
2178
|
+
.then((all) => all.filter((filing) => filingFormMatches(filing.form, form)).slice(0, count))
|
|
2179
|
+
: market.dataProvider.getSecFilings(symbol, count, exchange, context),
|
|
2180
|
+
(found) => found.length === 0),
|
|
2181
|
+
quotePromise,
|
|
2182
|
+
]);
|
|
2183
|
+
const resolved = identity ?? listingIdentity(listing, quote);
|
|
2184
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
2185
|
+
// A company on ASX that files nothing with the SEC lists its ASX announcements instead.
|
|
2186
|
+
if (identity && filings.length === 0 && !form && listsExchangeAnnouncements(identity.exchange)) {
|
|
2187
|
+
await printExchangeAnnouncements(ctx, { resolved, count, choice, page: page! });
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
ctx.printResult({
|
|
2191
|
+
data: filingsWithItemLabels(filings),
|
|
2192
|
+
metadata: {
|
|
2193
|
+
...listingMetadata(resolved), ...(form ? { form } : {}),
|
|
2194
|
+
...(page ? { announcementsPage: page.url } : {}),
|
|
2195
|
+
},
|
|
2196
|
+
freshness: rowsFreshness(filingRows(filings), { ...SEC_FILINGS, observedKey: "filingDate" }),
|
|
2197
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
2198
|
+
}, {
|
|
2199
|
+
heading: listingHeading(resolved),
|
|
2200
|
+
rows: filingRows,
|
|
2201
|
+
columns: FILING_COLUMNS,
|
|
2202
|
+
textColumns: FILING_TEXT_COLUMNS,
|
|
2203
|
+
// A listing abroad is looked up by its company: none found means the company files nothing with the SEC,
|
|
2204
|
+
// and the page its own exchange publishes its announcements on is where to read them.
|
|
2205
|
+
empty: [form ? `No ${form} filings found for ${listingTitle(resolved)}.`
|
|
2206
|
+
: identity ? noSecReportsNotice(resolved)
|
|
2207
|
+
: `No SEC filings found for ${listingTitle(resolved)}.`, pageLine].filter(Boolean).join("\n"),
|
|
2208
|
+
...(pageLine ? { footnote: pageLine, exportNotes: [pageLine] } : {}),
|
|
2209
|
+
});
|
|
2210
|
+
} catch (error) {
|
|
2211
|
+
if (!(error instanceof SecRegistrantMismatchError)) throw error;
|
|
2212
|
+
// Another company's filings never show under this listing; say whose they were.
|
|
2213
|
+
const message = secRegistrantMismatchMessage(
|
|
2214
|
+
error.listing, error.registrantName, await registrantUsExchange(market, error.listing.symbol, error.registrantName),
|
|
2215
|
+
);
|
|
2216
|
+
const resolved = identity ?? listingIdentity(listing, await quotePromise);
|
|
2217
|
+
ctx.printResult({
|
|
2218
|
+
data: [] as SecFilingItem[],
|
|
2219
|
+
metadata: { ...listingMetadata(resolved), secRegistrant: error.registrantName, ...(page ? { announcementsPage: page.url } : {}) },
|
|
2220
|
+
warnings: ctx.cliOptions.format === "text" ? undefined : [message],
|
|
2221
|
+
}, { rows: filingRows, columns: FILING_COLUMNS, textColumns: FILING_TEXT_COLUMNS, empty: [message, pageLine].filter(Boolean).join("\n") });
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
/**
|
|
2227
|
+
* `filings` for a company on ASX that files nothing with the SEC: its latest
|
|
2228
|
+
* ASX announcements from Gloom Cloud, newest first, under the line saying it
|
|
2229
|
+
* files no SEC reports, with its ASX announcements page. A failed read still
|
|
2230
|
+
* ends on that page, with the reason.
|
|
2231
|
+
*/
|
|
2232
|
+
async function printExchangeAnnouncements(
|
|
2233
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
2234
|
+
input: { resolved: ListingIdentity; count: number; choice: string[]; page: ExchangeAnnouncementsPage },
|
|
2235
|
+
): Promise<void> {
|
|
2236
|
+
const { resolved, count, choice, page } = input;
|
|
2237
|
+
const notice = noSecReportsNotice(resolved);
|
|
2238
|
+
const pageLine = `${page.name}: ${page.url}`;
|
|
2239
|
+
let payload: ExchangeAnnouncementsPayload | null = null;
|
|
2240
|
+
let failure: string | null = null;
|
|
2241
|
+
try {
|
|
2242
|
+
payload = await apiClient.getCloudExchangeAnnouncements({ symbol: resolved.symbol, exchange: resolved.exchange, limit: Math.min(count, 50) });
|
|
2243
|
+
} catch (error) {
|
|
2244
|
+
failure = `ASX announcements unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
2245
|
+
}
|
|
2246
|
+
const rows = payload ? announcementRows(payload) : [];
|
|
2247
|
+
const warnings = [
|
|
2248
|
+
...choice,
|
|
2249
|
+
...payload?.stale ? ["ASX could not be reached; these are the announcements Gloom Cloud read last."] : [],
|
|
2250
|
+
...failure ? [failure] : [],
|
|
2251
|
+
];
|
|
2252
|
+
ctx.printResult({
|
|
2253
|
+
data: rows,
|
|
2254
|
+
metadata: {
|
|
2255
|
+
...listingMetadata(resolved), secFilings: 0, announcementsPage: payload?.page ?? page.url,
|
|
2256
|
+
...(payload ? { announcementsTotal: payload.total, announcementsRetrievedAt: payload.retrievedAt } : {}),
|
|
2257
|
+
},
|
|
2258
|
+
freshness: rowsFreshness(rows, { ...ASX_ANNOUNCEMENTS, observedKey: "date" }),
|
|
2259
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
2260
|
+
}, {
|
|
2261
|
+
heading: listingHeading(resolved),
|
|
2262
|
+
...(rows.length > 0 ? { summary: () => `${notice} Its latest ASX announcements:` } : {}),
|
|
2263
|
+
rows: () => rows,
|
|
2264
|
+
columns: ANNOUNCEMENT_COLUMNS,
|
|
2265
|
+
empty: `${failure ? notice : `${notice} ASX lists no announcements for it.`}\n${pageLine}`,
|
|
2266
|
+
footnote: pageLine,
|
|
2267
|
+
exportNotes: [notice, pageLine],
|
|
2268
|
+
exportIdentity: { symbolOrSeries: `${resolved.symbol}:${resolved.exchange}` },
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
type Form4Insiders = "available" | "none" | "foreign-private-issuer" | "no-sec-reports";
|
|
2273
|
+
|
|
2274
|
+
/** The company's own Form 4 filings as `fn INS` reads them: whether it has any, and its summary of them. */
|
|
2275
|
+
interface Form4Report {
|
|
2276
|
+
status: Form4Insiders;
|
|
2277
|
+
/** `fn INS`'s line of the last 90 days' open-market buys and sales in the filings read; null when it has none. */
|
|
2278
|
+
summary: string | null;
|
|
2279
|
+
/** The Form 4 lines in the filings read, and how many filings that was. */
|
|
2280
|
+
lines: number;
|
|
2281
|
+
filings: number;
|
|
2282
|
+
/** The newest filing's date (YYYY-MM-DD), which dates the report. */
|
|
2283
|
+
newestFiled: string | null;
|
|
2284
|
+
/** "Canadian insider reports (SEDI) not covered." beside the reason a company from abroad has none. */
|
|
2285
|
+
home: string | null;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
/**
|
|
2289
|
+
* The company's Form 4 filings, read as `fn INS` reads its first page (its
|
|
2290
|
+
* latest 20 of its own) and summed up as it sums them; null when they could
|
|
2291
|
+
* not be read. A listing abroad the SEC lists nothing for files no SEC
|
|
2292
|
+
* reports at all, and a company from abroad with none names its home
|
|
2293
|
+
* market's insider reports as not covered either.
|
|
2294
|
+
*/
|
|
2295
|
+
async function loadForm4Report(market: MarketContext, listing: CliListing): Promise<Form4Report | null> {
|
|
2296
|
+
const { symbol, exchange } = listing.request;
|
|
2297
|
+
try {
|
|
2298
|
+
const { parsed, foreignPrivateIssuer, noFilings } = await loadParsedInsiderFilings(market.dataProvider, symbol, {
|
|
2299
|
+
limit: INSIDER_REPORT_FILINGS,
|
|
2300
|
+
exchange,
|
|
2301
|
+
});
|
|
2302
|
+
if (parsed.length > 0) {
|
|
2303
|
+
const filed = parsed.map(({ filing }) => new Date(filing.filingDate).getTime()).filter(Number.isFinite);
|
|
2304
|
+
return {
|
|
2305
|
+
status: "available",
|
|
2306
|
+
summary: buildInsiderSummary(parsed, Date.now(), parsed),
|
|
2307
|
+
lines: parsed.filter(({ transaction }) => transaction != null).length,
|
|
2308
|
+
filings: new Set(parsed.map(({ filing }) => filing.accessionNumber)).size,
|
|
2309
|
+
newestFiled: filed.length > 0 ? new Date(Math.max(...filed)).toISOString().slice(0, 10) : null,
|
|
2310
|
+
home: null,
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
const status: Form4Insiders = noFilings && nonUsSecListingVenue(symbol, exchange) ? "no-sec-reports"
|
|
2314
|
+
: foreignPrivateIssuer ? "foreign-private-issuer" : "none";
|
|
2315
|
+
const home = await loadHomeInsiderReportsNote(market.dataProvider, listing.key, exchange, foreignPrivateIssuer);
|
|
2316
|
+
return { status, summary: null, lines: 0, filings: 0, newestFiled: null, home };
|
|
2317
|
+
} catch (error) {
|
|
2318
|
+
logFailure("cli.commands-market", "Read insider filings failed")(error);
|
|
2319
|
+
return null;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
/**
|
|
2324
|
+
* The insider lines under `insider`'s holders. A company's insiders file
|
|
2325
|
+
* Form 4, which 13F holder lists rarely name, so the report leads with
|
|
2326
|
+
* `fn INS`'s summary of the Form 4 filings and points to the transactions;
|
|
2327
|
+
* a company with none says why, in the words `fn INS` uses. The insider share
|
|
2328
|
+
* of the company follows when the holder data reports it.
|
|
2329
|
+
*/
|
|
2330
|
+
function insiderSummary(
|
|
2331
|
+
data: HolderData,
|
|
2332
|
+
listing: Pick<CliListing, "key">,
|
|
2333
|
+
identity: ListingIdentity,
|
|
2334
|
+
form4: Form4Report | null,
|
|
2335
|
+
): string {
|
|
2336
|
+
const held = data.summary?.insidersPercentHeld;
|
|
2337
|
+
const share = held == null || !Number.isFinite(held)
|
|
2338
|
+
? ""
|
|
2339
|
+
: `Insiders hold ${formatFractionPercentCell(held)} of shares outstanding.`;
|
|
2340
|
+
// The listing as the command read it (CAR:ASX), not the source's spelling of it (CAR.AX), which fn does not take.
|
|
2341
|
+
const transactions = form4?.status === "available"
|
|
2342
|
+
? [
|
|
2343
|
+
...form4.summary ? [form4.summary] : [],
|
|
2344
|
+
`${form4.lines} Form 4 ${form4.lines === 1 ? "line" : "lines"} in the latest ${form4.filings} ${form4.filings === 1 ? "filing" : "filings"}${form4.newestFiled ? `, newest filed ${form4.newestFiled}` : ""}: gloom fn INS ${listing.key}`,
|
|
2345
|
+
].join("\n")
|
|
2346
|
+
: form4?.status === "no-sec-reports" ? [noSecReportsNotice(identity), form4.home].filter(Boolean).join(" ")
|
|
2347
|
+
: form4 ? insiderEmptyNotice(form4.status === "foreign-private-issuer", form4.home) : "";
|
|
2348
|
+
return [transactions, share].filter(Boolean).join("\n");
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
/** `holders --form 13d|13g|all`: the 13D/13G beneficial owners, joined by name to the 13F holders. */
|
|
2352
|
+
async function printBeneficialOwners(
|
|
2353
|
+
listing: CliListing,
|
|
2354
|
+
market: MarketContext,
|
|
2355
|
+
form: Exclude<HolderForm, "13f">,
|
|
2356
|
+
history: boolean,
|
|
2357
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
2358
|
+
) {
|
|
2359
|
+
const { symbol, exchange } = listing.request;
|
|
2360
|
+
// A listing outside the US sends its company to the lookup, which needs its quote first.
|
|
2361
|
+
const quotePromise = loadListingQuote(market.dataProvider, listing);
|
|
2362
|
+
const abroad = nonUsSecListingVenue(symbol, exchange) ? listingIdentity(listing, await quotePromise) : null;
|
|
2363
|
+
const request = { form: beneficialRouteForm(form), history, listing: { exchange, name: abroad?.name ?? undefined } };
|
|
2364
|
+
const [payload, holders, quote, financials] = await Promise.all([
|
|
2365
|
+
loadForListing(listing, market, ctx, () => fetchBeneficialOwners(symbol, request)),
|
|
2366
|
+
market.dataProvider.getHolders?.(symbol, exchange).catch(logFailure("cli.commands-market", "Read company holders failed", null)) ?? null,
|
|
2367
|
+
quotePromise,
|
|
2368
|
+
// Today's share count, which the filed percents are set against.
|
|
2369
|
+
market.dataProvider.getTickerFinancials(symbol, exchange).catch(logFailure("cli.commands-market", "Read company financials for holders failed", null)),
|
|
2370
|
+
]);
|
|
2371
|
+
const identity = listingIdentity(listing, quote);
|
|
2372
|
+
const sharesOutstanding = classSharesToday(financials);
|
|
2373
|
+
const rows = buildBeneficialReportRows(payload, { history, holders, sharesOutstanding });
|
|
2374
|
+
const notices = beneficialCoverageNotices(payload.coverage);
|
|
2375
|
+
const sharesOutstandingAsOf = classSharesAsOf(financials);
|
|
2376
|
+
const legend = beneficialReportLegend(rows, sharesOutstanding, sharesOutstandingAsOf);
|
|
2377
|
+
ctx.printResult({
|
|
2378
|
+
data: rows,
|
|
2379
|
+
...(notices.length ? { warnings: notices } : {}),
|
|
2380
|
+
metadata: {
|
|
2381
|
+
...listingMetadata(identity),
|
|
2382
|
+
name: identity.name ?? (payload.companyName || null),
|
|
2383
|
+
cik: payload.cik || null,
|
|
2384
|
+
form,
|
|
2385
|
+
history,
|
|
2386
|
+
asOf: payload.asOf,
|
|
2387
|
+
coverage: payload.coverage,
|
|
2388
|
+
complete: beneficialListComplete(payload),
|
|
2389
|
+
sharesOutstanding,
|
|
2390
|
+
...(sharesOutstandingAsOf ? { sharesOutstandingAsOf } : {}),
|
|
2391
|
+
},
|
|
2392
|
+
}, {
|
|
2393
|
+
heading: `${listingHeading(identity)}${cliStyles.muted(beneficialListFacts(payload, form, rows.length, history).map((fact) => ` · ${fact}`).join(""))}`,
|
|
2394
|
+
// Text draws each column as the HDS report does; CSV and JSON keep the raw values.
|
|
2395
|
+
textColumns: BENEFICIAL_REPORT_COLUMNS.map((column) => ({ ...column, format: column.textFormat ?? column.format })),
|
|
2396
|
+
...(legend ? { footnote: legend } : {}),
|
|
2397
|
+
columns: [
|
|
2398
|
+
...BENEFICIAL_REPORT_COLUMNS.map(({ key, header, align }) => ({ key, header, ...(align ? { align } : {}) })),
|
|
2399
|
+
{ key: "otherPersons", header: "Other persons" },
|
|
2400
|
+
{ key: "status", header: "Status" },
|
|
2401
|
+
{ key: "filerCik", header: "Filer CIK" },
|
|
2402
|
+
{ key: "accessionNumber", header: "Accession" },
|
|
2403
|
+
{ key: "filingUrl", header: "URL" },
|
|
2404
|
+
],
|
|
2405
|
+
empty: beneficialListUnreadable(payload)
|
|
2406
|
+
? `13D/13G filings for ${listingTitle(identity)} are listed but could not be read.`
|
|
2407
|
+
// The SEC knows no company for a listing abroad that files there under no name of its own.
|
|
2408
|
+
: abroad && !payload.cik ? noSecReportsNotice(identity)
|
|
2409
|
+
: `No 13D/13G filings in the last 4 years for ${listingTitle(identity)}.`,
|
|
2410
|
+
});
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
/** Holder rows `holders` and `13f` print without `--limit`, as fn HDS does. */
|
|
2414
|
+
const HOLDERS_DEFAULT_ROWS = 25;
|
|
2415
|
+
|
|
2416
|
+
async function runHolders(
|
|
2417
|
+
rawArgs: string[],
|
|
2418
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
2419
|
+
commandName: string,
|
|
2420
|
+
ownerTypes?: Set<string>,
|
|
2421
|
+
) {
|
|
2422
|
+
const args = [...rawArgs];
|
|
2423
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
2424
|
+
const formOption = commandName === "holders" ? takeOption(args, "--form") : undefined;
|
|
2425
|
+
const history = commandName === "holders" && takeFlag(args, "--history");
|
|
2426
|
+
const raw = requireOneArg(args, `${commandName} <symbol>`, "symbol", ctx);
|
|
2427
|
+
const form = formOption == null ? "13f" : parseHolderForm(formOption);
|
|
2428
|
+
if (!form) ctx.fail(`Unknown form "${formOption}".`, `Use one of ${HOLDER_FORMS.join(", ")}.`);
|
|
2429
|
+
if (form === "13f" && history) ctx.fail("--history lists 13D/13G reports.", "Add --form 13d, 13g or all.");
|
|
2430
|
+
await withMarketData(ctx, async (market) => {
|
|
2431
|
+
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
2432
|
+
if (form !== "13f") return printBeneficialOwners(listing, market, form, history, ctx);
|
|
2433
|
+
const insiderCommand = commandName === "insider" && ownerTypes != null;
|
|
2434
|
+
const institutional = !ownerTypes?.has("insider");
|
|
2435
|
+
const venues = loadBareVenues(listing, market);
|
|
2436
|
+
// A US line's institutions are every 13F filer, from the SEC holder index as fn 13F lists them,
|
|
2437
|
+
// as many as the limit asks (HDS's 25 by default); elsewhere, or when the index cannot be read, the source's few.
|
|
2438
|
+
const indexTicker = institutional ? indexHolderTicker(listing.request.symbol, listing.request.exchange) : null;
|
|
2439
|
+
// --tail counts back from the end of the rows loaded, so it loads at least as many as it asks for.
|
|
2440
|
+
const tail = ctx.cliOptions.tail;
|
|
2441
|
+
const indexLimit = Math.min(ctx.cliOptions.limit ?? Math.max(tail ?? 0, HOLDERS_DEFAULT_ROWS), HOLDER_ROWS_MAX);
|
|
2442
|
+
const quoted = loadListingQuote(market.dataProvider, listing);
|
|
2443
|
+
const [source, quote, form4, index] = await Promise.all([
|
|
2444
|
+
loadForListing(
|
|
2445
|
+
listing, market, ctx,
|
|
2446
|
+
() => market.dataProvider.getHolders(listing.request.symbol, listing.request.exchange),
|
|
2447
|
+
(found) => found.holders.length === 0,
|
|
2448
|
+
{ command: commandName, noun: "holder data" },
|
|
2449
|
+
).catch(async (error: unknown) => {
|
|
2450
|
+
// A fund has no ownership breakdown: say so, and what reads it, rather than the source's NO_DATA.
|
|
2451
|
+
const fund = await fundHoldersAnswer(listing.symbol, error, await quoted, apiClient);
|
|
2452
|
+
if (fund) fail(fund.message, fund.details.join("\n") || undefined, NO_DATA_CODE);
|
|
2453
|
+
throw error;
|
|
2454
|
+
}),
|
|
2455
|
+
quoted,
|
|
2456
|
+
insiderCommand ? loadForm4Report(market, listing) : null,
|
|
2457
|
+
indexTicker ? loadIndexedHolders(indexTicker, indexLimit).catch(logFailure("cli.commands-market", "Read the 13F holder index failed", null)) : null,
|
|
2458
|
+
]);
|
|
2459
|
+
const indexed = !!index?.rows.length && indexHolderTicker(listing.request.symbol, listing.request.exchange, source) != null;
|
|
2460
|
+
const data = indexed
|
|
2461
|
+
? withIndexedHolders(source, indexTicker!, { ...index!, rows: index!.rows.slice(0, indexLimit) }, indexedHolderUnits(source, quote?.price))
|
|
2462
|
+
: source;
|
|
2463
|
+
const identity = listingIdentity(listing, quote);
|
|
2464
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
2465
|
+
// A London line's values can be dollars from the 13F filings; the listing's currency is not their unit.
|
|
2466
|
+
const valueCurrency = holderValueCurrency(data, "USD")!;
|
|
2467
|
+
// The rows, their units and their order are HDS's, so `holders --csv` and `fn HDS --csv` agree.
|
|
2468
|
+
const rows = holderReportRows(data, {
|
|
2469
|
+
marketCap: holderStakeMarketCap(quote?.marketCap, quote?.currency, valueCurrency),
|
|
2470
|
+
ownerTypes,
|
|
2471
|
+
});
|
|
2472
|
+
const shownRows = windowRows(rows, ctx.cliOptions).rows;
|
|
2473
|
+
const shown = shownRows.length;
|
|
2474
|
+
const total = institutional ? data.summary?.institutionsCount ?? null : null;
|
|
2475
|
+
const reportDate = sharedReportDate(rows);
|
|
2476
|
+
const valueBasis = rows.length > 0 ? holderValueBasis(reportDate, data.valueBasis, valueCurrency) : null;
|
|
2477
|
+
const positionsBasis = rows.length > 0 ? nonUsHolderCaveat(identity.exchange || data.exchange, data.currency) : null;
|
|
2478
|
+
const shareBasisNote = holderShareBasisNote(data, rows);
|
|
2479
|
+
const markedRows = rows.some((row) => holderShareBasisMarker(row));
|
|
2480
|
+
// The heading names the listing; its unit, date and how much of the list follows on the same line.
|
|
2481
|
+
// One "as of" only when every row shown is of that date; each row carries its own.
|
|
2482
|
+
// An insider list the 13F filings name nobody in is not "none reported": the Form 4 summary under it says what insiders did.
|
|
2483
|
+
const form4Only = insiderCommand && rows.length === 0;
|
|
2484
|
+
const facts = form4Only ? [] : holderListFacts({
|
|
2485
|
+
currency: valueCurrency,
|
|
2486
|
+
listingCurrency: data.currency,
|
|
2487
|
+
period: holderListPeriod(shownRows, data.asOf),
|
|
2488
|
+
shown,
|
|
2489
|
+
reported: rows.length,
|
|
2490
|
+
total,
|
|
2491
|
+
ranked: !positionsBasis,
|
|
2492
|
+
});
|
|
2493
|
+
const unvaluedNote = unvaluedHoldersNote(shownRows);
|
|
2494
|
+
const columns = holderReportColumns(shownRows, valueCurrency);
|
|
2495
|
+
// Dated as HDS dates it: by the list's as-of; an insider report of Form 4 filings alone as INS dates it, by the newest.
|
|
2496
|
+
const freshness = form4Only
|
|
2497
|
+
? rowsFreshness(form4?.newestFiled ? [{ filingDate: form4.newestFiled }] : [], { ...SEC_FILINGS, basis: "Form 4 filings", observedKey: "filingDate" })
|
|
2498
|
+
: rowsFreshness(shownRows, { ...SEC_FILINGS, basis: "13F filings" }, { asOf: data.asOf ?? null });
|
|
2499
|
+
// Named as fn HDS takes it, so the closing notes point to the same listing.
|
|
2500
|
+
const requested = exchangeOption && !raw.includes(":") ? `${raw}:${exchangeOption.toUpperCase()}` : raw;
|
|
2501
|
+
const { notices } = holderReportNotices({ data, requested, shown: shownRows, reported: rows.length, total, indexed });
|
|
2502
|
+
// The index's smallest filers are pages past what was loaded: --tail is the end of the loaded rows, not of every filer.
|
|
2503
|
+
const tailNote = indexed && tail != null && total != null && rows.length < total
|
|
2504
|
+
? `--tail ${tail} is the last ${shown} of the largest ${rows.length} loaded, not the smallest of all ${total.toLocaleString("en-US")} holders.`
|
|
2505
|
+
: null;
|
|
2506
|
+
const partialNote = indexed ? indexedHoldersPartialNote(index, indexLimit) : null;
|
|
2507
|
+
const warnings = [...choice, ...(partialNote ? [partialNote] : []), ...(tailNote ? [tailNote] : [])];
|
|
2508
|
+
ctx.printResult({
|
|
2509
|
+
data: rows,
|
|
2510
|
+
...(freshness ? { freshness } : {}),
|
|
2511
|
+
metadata: {
|
|
2512
|
+
...listingMetadata(identity),
|
|
2513
|
+
summary: data.summary ?? null,
|
|
2514
|
+
currency: data.currency ?? null,
|
|
2515
|
+
valueCurrency,
|
|
2516
|
+
units: holderReportUnits(valueCurrency),
|
|
2517
|
+
asOf: data.asOf ?? null,
|
|
2518
|
+
// Each holder's latest period; several when the list mixes quarters.
|
|
2519
|
+
reportDates: holderReportDates(shownRows),
|
|
2520
|
+
shown,
|
|
2521
|
+
reported: rows.length,
|
|
2522
|
+
total,
|
|
2523
|
+
truncated: shown < (total ?? rows.length),
|
|
2524
|
+
valueBasis,
|
|
2525
|
+
positionsBasis,
|
|
2526
|
+
...(insiderCommand ? {
|
|
2527
|
+
form4Insiders: form4?.status ?? null,
|
|
2528
|
+
// INS's summary of the latest Form 4 filings, as `fn INS` gives it in its metadata.
|
|
2529
|
+
form4: form4 ? { summary: form4.summary, lines: form4.lines, filings: form4.filings, newestFiled: form4.newestFiled, notCovered: form4.home } : null,
|
|
2530
|
+
} : {}),
|
|
2531
|
+
},
|
|
2532
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
2533
|
+
}, {
|
|
2534
|
+
heading: `${listingHeading(identity)}${cliStyles.muted(facts.map((fact) => ` · ${fact}`).join(""))}`,
|
|
2535
|
+
columns,
|
|
2536
|
+
textColumns: [
|
|
2537
|
+
{ key: "ownerType", header: "Type" },
|
|
2538
|
+
{ key: "name", header: "Holder" },
|
|
2539
|
+
{ key: "reportDate", header: "Date" },
|
|
2540
|
+
{ key: "shares", header: "Shares", align: "right", format: formatCountCell },
|
|
2541
|
+
// Marks the rows of a home line held as receipts; absent when no row is.
|
|
2542
|
+
...(markedRows ? [{ key: "shareBasis", header: "Basis", value: (row: Record<string, unknown>) => holderShareBasisMarker(row) }] : []),
|
|
2543
|
+
{
|
|
2544
|
+
key: "value",
|
|
2545
|
+
header: moneyColumnHeader("Value", valueCurrency),
|
|
2546
|
+
align: "right",
|
|
2547
|
+
value: (row) => row.value == null ? "" : formatCompact(Number(row.value)),
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
key: "percentHeld",
|
|
2551
|
+
header: "% Held",
|
|
2552
|
+
align: "right",
|
|
2553
|
+
format: (value) => isFiniteNumber(value) ? formatPercentHeld(value) : "",
|
|
2554
|
+
},
|
|
2555
|
+
],
|
|
2556
|
+
// CSV and NDJSON are the HDS report's table and closing lines.
|
|
2557
|
+
tables: {
|
|
2558
|
+
tables: [exportRowsTable("Holders", columns, shownRows)],
|
|
2559
|
+
footer: reportFooterLines({ freshness, notes: notices }),
|
|
2560
|
+
},
|
|
2561
|
+
exportIdentity: { symbolOrSeries: listing.key },
|
|
2562
|
+
summary: () => [
|
|
2563
|
+
[valueBasis ? `Value = ${valueBasis}.` : "", positionsBasis ?? "", shareBasisNote ?? "", unvaluedNote ?? ""].filter(Boolean).join(" "),
|
|
2564
|
+
insiderCommand ? insiderSummary(data, listing, identity, form4) : "",
|
|
2565
|
+
].filter(Boolean).join("\n"),
|
|
2566
|
+
empty: `No holders reported for ${listingTitle(identity)}.`,
|
|
2567
|
+
});
|
|
2568
|
+
});
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
2572
|
+
const args = [...rawArgs];
|
|
2573
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
2574
|
+
const raw = requireOneArg(args, "analyst <symbol>", "symbol", ctx);
|
|
2575
|
+
await withMarketData(ctx, async (market) => {
|
|
2576
|
+
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
2577
|
+
const venues = loadBareVenues(listing, market);
|
|
2578
|
+
const [data, quote] = await Promise.all([
|
|
2579
|
+
loadForListing(
|
|
2580
|
+
listing, market, ctx, () => market.dataProvider.getAnalystResearch(listing.request.symbol, listing.request.exchange),
|
|
2581
|
+
undefined, { command: "analyst", noun: "analyst research" },
|
|
2582
|
+
),
|
|
2583
|
+
loadListingQuote(market.dataProvider, listing),
|
|
2584
|
+
]);
|
|
2585
|
+
const identity = listingIdentity(listing, quote);
|
|
2586
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
2587
|
+
const { upside, reference } = analystUpside(data, quote);
|
|
2588
|
+
ctx.printResult({
|
|
2589
|
+
data,
|
|
2590
|
+
metadata: {
|
|
2591
|
+
...listingMetadata(identity),
|
|
2592
|
+
recommendationRating: data.recommendationRating,
|
|
2593
|
+
priceTarget: data.priceTarget,
|
|
2594
|
+
recommendations: data.recommendations,
|
|
2595
|
+
upside: upside ?? null,
|
|
2596
|
+
upsideReference: analystReferenceMetadata(reference),
|
|
2597
|
+
},
|
|
2598
|
+
freshness: rowsFreshness(analystRows(data), {
|
|
2599
|
+
...REPORTED_DATA, basis: "analyst ratings", observedKey: "date", oldest: null,
|
|
2600
|
+
}, { stale: data.stale === true }),
|
|
2601
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
2602
|
+
}, {
|
|
2603
|
+
heading: listingHeading(identity),
|
|
2604
|
+
rows: analystRows,
|
|
2605
|
+
summary: (research) => analystSummary(research, quote),
|
|
2606
|
+
columns: [
|
|
2607
|
+
{ key: "date", header: "Date" },
|
|
2608
|
+
{ key: "firm", header: "Firm" },
|
|
2609
|
+
{ key: "action", header: "Action" },
|
|
2610
|
+
{ key: "current", header: "Current" },
|
|
2611
|
+
{ key: "target", header: "Target", align: "right" },
|
|
2612
|
+
],
|
|
2613
|
+
});
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
2618
|
+
const args = [...rawArgs];
|
|
2619
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
2620
|
+
// A central bank's name means the bank, not a listing that shares it; a venue still reaches the listing.
|
|
2621
|
+
const bank = exchangeOption ? null : centralBankEventsMessage(args.join(" "));
|
|
2622
|
+
if (bank) ctx.fail(bank);
|
|
2623
|
+
const raw = requireOneArg(args, "events <symbol>", "symbol", ctx);
|
|
2624
|
+
await withMarketData(ctx, async (market) => {
|
|
2625
|
+
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
2626
|
+
const venues = loadBareVenues(listing, market);
|
|
2627
|
+
// The earnings rows are resolved against the calendar `earnings` reads, so the next report is the same in both.
|
|
2628
|
+
const calendar = market.dataProvider.getEarningsCalendar?.([listing.key])
|
|
2629
|
+
.catch(logFailure("cli.commands-market", "Read the earnings calendar for events failed", [] as EarningsEvent[]));
|
|
2630
|
+
const [recorded, quote, events] = await Promise.all([
|
|
2631
|
+
loadForListing(
|
|
2632
|
+
listing, market, ctx, () => market.dataProvider.getCorporateActions(listing.request.symbol, listing.request.exchange),
|
|
2633
|
+
undefined, { command: "events", noun: "corporate events" },
|
|
2634
|
+
),
|
|
2635
|
+
loadListingQuote(market.dataProvider, listing),
|
|
2636
|
+
calendar,
|
|
2637
|
+
]);
|
|
2638
|
+
const data = withResolvedEarnings(recorded, calendarEventOf(events, listing.key), newYorkDay());
|
|
2639
|
+
const identity = listingIdentity(listing, quote);
|
|
2640
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
2641
|
+
ctx.printResult({
|
|
2642
|
+
data,
|
|
2643
|
+
metadata: listingMetadata(identity),
|
|
2644
|
+
// Dated by when it was read, as EE and fn EVT date the same events: a row's
|
|
2645
|
+
// date is when the event happens, and the newest past one is no as-of.
|
|
2646
|
+
freshness: rowsFreshness([], { ...REPORTED_DATA, basis: "corporate actions", oldest: null }),
|
|
2647
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
2648
|
+
}, {
|
|
2649
|
+
heading: listingHeading(identity),
|
|
2650
|
+
rows: corporateActionRows,
|
|
2651
|
+
columns: [
|
|
2652
|
+
{ key: "date", header: "Date" },
|
|
2653
|
+
{ key: "type", header: "Type" },
|
|
2654
|
+
{ key: "detail", header: "Detail" },
|
|
2655
|
+
],
|
|
2656
|
+
});
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
2661
|
+
const args = [...rawArgs];
|
|
2662
|
+
const leapsOptions = takeLeapsOptions(args);
|
|
2663
|
+
const expirationOption = takeExpirationOption(args);
|
|
2664
|
+
const { value: expiration, flag: expirationFlag } = expirationOption;
|
|
2665
|
+
if (expirationOption.both) ctx.fail("--expiry is --expiration; give one of them.", `Usage: gloom ${OPTIONS_USAGE}`);
|
|
2666
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
2667
|
+
if (leapsOptions.leaps) {
|
|
2668
|
+
// With --leaps, --expiration is a window of one expiry.
|
|
2669
|
+
const criteria = parseLeapsCriteria({ ...leapsOptions.raw, ...expirationOption.given ? { "--expiration": expiration ?? "" } : {} });
|
|
2670
|
+
if ("error" in criteria) ctx.fail(criteria.error, `Usage: gloom ${LEAPS_USAGE}`);
|
|
2671
|
+
const entries = normalizeSymbols(args);
|
|
2672
|
+
if (entries.length === 0) ctx.fail("--leaps needs at least one symbol.", `Usage: gloom ${LEAPS_USAGE}`);
|
|
2673
|
+
await withMarketData(ctx, async (market) => runLeapsScreen(await readSymbols(entries, market.dataProvider), exchangeOption, criteria, market, ctx));
|
|
2674
|
+
return;
|
|
2675
|
+
}
|
|
2676
|
+
if (leapsOptions.given.length > 0) {
|
|
2677
|
+
ctx.fail(`${leapsOptions.given.join(", ")} only ${leapsOptions.given.length > 1 ? "apply" : "applies"} with --leaps.`, `Usage: gloom ${LEAPS_USAGE}`);
|
|
2678
|
+
}
|
|
2679
|
+
const raw = requireOneArg(args, OPTIONS_USAGE, "symbol", ctx);
|
|
2680
|
+
let expirationDate: number | undefined;
|
|
2681
|
+
if (expirationOption.given) {
|
|
2682
|
+
const parsed = expiration == null ? null : parseOptionExpiration(expiration);
|
|
2683
|
+
if (parsed == null) {
|
|
2684
|
+
ctx.fail(
|
|
2685
|
+
expiration == null ? `${expirationFlag} needs a date.` : `Invalid ${expirationFlag} "${expiration}".`,
|
|
2686
|
+
`Use ${OPTION_EXPIRATION_FORMAT}.${expiration == null ? "" : expirationHint(expiration)}\nUsage: gloom ${OPTIONS_USAGE}`,
|
|
2687
|
+
);
|
|
2688
|
+
}
|
|
2689
|
+
expirationDate = parsed;
|
|
2690
|
+
}
|
|
2691
|
+
await withMarketData(ctx, async (market) => {
|
|
2692
|
+
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
2693
|
+
const { symbol, exchange } = listing.request;
|
|
2694
|
+
const refresh = ctx.cliOptions.refresh;
|
|
2695
|
+
const venues = loadBareVenues(listing, market);
|
|
2696
|
+
const quotePromise = loadListingQuote(market.dataProvider, listing);
|
|
2697
|
+
const fate = listingFateLoader(listing, market, quotePromise);
|
|
2698
|
+
const inputsPromise = loadOptionModelInputs(market.dataProvider, listing, quotePromise, refresh);
|
|
2699
|
+
const now = Date.now();
|
|
2700
|
+
// Expiries close on the listing's clock: a coin's options (Deribit) settle at 08:00 UTC on any day.
|
|
2701
|
+
const expiryVenue = optionExpiryVenue(symbol, exchange);
|
|
2702
|
+
const readChain = async (expiry: number | undefined) => {
|
|
2703
|
+
const cached = await market.dataProvider.getCachedQuery?.("getOptionsChain", [symbol, exchange, expiry, undefined])
|
|
2704
|
+
.load({ force: refresh });
|
|
2705
|
+
return {
|
|
2706
|
+
result: cached,
|
|
2707
|
+
chain: cached?.value ?? await market.dataProvider.getOptionsChain(symbol, exchange, expiry, {
|
|
2708
|
+
cacheMode: refresh ? "refresh" : "default",
|
|
2709
|
+
}),
|
|
2710
|
+
};
|
|
2711
|
+
};
|
|
2712
|
+
// An expiry whose last session has closed has no chain to show: it says so, and names the nearest live one.
|
|
2713
|
+
if (expirationDate != null && !isLiveOptionExpiry(expirationDate, now, expiryVenue)) {
|
|
2714
|
+
const listed = await readChain(undefined).then(({ chain }) => chain.expirationDates,
|
|
2715
|
+
logFailure("cli-market", "Read available option expirations failed", []));
|
|
2716
|
+
ctx.fail(`expiry ${expiryIsoDate(expirationDate)} for ${listing.key} `
|
|
2717
|
+
+ expiredExpiryText(expirationDate, listed, now, { exchange: expiryVenue, flag: expirationFlag.slice(2) }),
|
|
2718
|
+
`Run gloom options ${listing.key} to list the live expiries.`);
|
|
2719
|
+
}
|
|
2720
|
+
const { result, chain: read } = await loadForListing(listing, market, ctx, async () => {
|
|
2721
|
+
const first = await readChain(expirationDate);
|
|
2722
|
+
// A source can still default to the expiry that lapsed at Friday's close; the nearest live one is shown instead.
|
|
2723
|
+
const live = expirationDate == null ? liveDefaultExpiry(first.chain, now, expiryVenue) : undefined;
|
|
2724
|
+
if (live === null) return { ...first, chain: { ...first.chain, calls: [], puts: [] } };
|
|
2725
|
+
if (live == null) return first;
|
|
2726
|
+
// The default's list stays the catalogue: a dated chain can list fewer expiries.
|
|
2727
|
+
const shown = await readChain(live);
|
|
2728
|
+
return { ...shown, chain: { ...shown.chain,
|
|
2729
|
+
expirationDates: [...new Set([...first.chain.expirationDates, ...shown.chain.expirationDates])].sort((a, b) => a - b) } };
|
|
2730
|
+
}, undefined, { command: "options", noun: "options chain", fate }).catch((error: unknown) => {
|
|
2731
|
+
if (!isOptionsUnavailableError(error)) throw error;
|
|
2732
|
+
// A coin other than BTC and ETH: say which coins have options, and the bitcoin-linked listings.
|
|
2733
|
+
if (isCoinWithoutOptions(listing.key)) return ctx.fail(optionsUnavailableTitle(listing.key), coinOptionsAlternativesText("options"));
|
|
2734
|
+
// A future or commodity with no chain of its own: name the listed fund whose options stand in.
|
|
2735
|
+
const alternative = listedOptionsAlternative(listing.key);
|
|
2736
|
+
// A name read as a fund or an index asks about the company's stock it means, whose options are listed.
|
|
2737
|
+
const meant = meantQuestion(listing);
|
|
2738
|
+
return ctx.fail(meant ? `${optionsUnavailableTitle(listing.key)} ${meant}` : optionsUnavailableTitle(listing.key), alternative
|
|
2739
|
+
? `Nearest listed alternative: ${alternative.symbol} (${alternative.name}), gloom options ${alternative.symbol}.`
|
|
2740
|
+
+ ` Strategy payoffs: gloom fn OSA ${alternative.symbol}`
|
|
2741
|
+
: "Strategy payoffs on a listed underlying: gloom fn OSA <ticker>");
|
|
2742
|
+
});
|
|
2743
|
+
const loaded = withLiveExpiryList(read, now, expiryVenue);
|
|
2744
|
+
const inputs = await inputsPromise;
|
|
2745
|
+
if (expirationDate != null && !chainHasExpiry(loaded, expirationDate)) {
|
|
2746
|
+
// An unlisted date comes back as an empty chain; its own list of expiries, else the default chain's, says what to pick.
|
|
2747
|
+
const listed = loaded.expirationDates.length > 0 ? loaded.expirationDates : await market.dataProvider
|
|
2748
|
+
.getOptionsChain(symbol, exchange, undefined, { cacheMode: "default" })
|
|
2749
|
+
.then((fallback) => liveOptionExpiries(fallback.expirationDates, now, expiryVenue),
|
|
2750
|
+
logFailure("cli-market", "Read available option expirations failed", []));
|
|
2751
|
+
const miss = missingExpiryMessage(listing.key, expirationDate, listed);
|
|
2752
|
+
ctx.fail(miss.message, miss.details);
|
|
2753
|
+
}
|
|
2754
|
+
const quote = await quotePromise;
|
|
2755
|
+
const identity = listingIdentity(listing, quote);
|
|
2756
|
+
// IV and delta are valued at one spot and instant, named under the table and on every exported row.
|
|
2757
|
+
const { chain, spot: modelSpot } = valueChainAtModelSpot(loaded, inputs, Date.now(), identity.exchange);
|
|
2758
|
+
const basis: OptionsChainBasis = { spot: modelSpot, dividendYield: inputs.dividendYield ?? null,
|
|
2759
|
+
openInterest: await loadOptionsChainOpenInterest(symbol, chain), ivFilled: chainHasFilledIv(chain) };
|
|
2760
|
+
const basisFields = optionsBasisFields(basis);
|
|
2761
|
+
const currency = [...chain.calls, ...chain.puts].find((contract) => contract.currency?.trim())?.currency.trim().toUpperCase()
|
|
2762
|
+
?? quote?.currency ?? null;
|
|
2763
|
+
// A failed refresh falls back to the stored chain, which can be days old.
|
|
2764
|
+
const refreshWarning = result?.refreshError == null ? null
|
|
2765
|
+
: `Options refresh failed; showing the chain stored ${new Date(result.fetchedAt).toISOString()}`
|
|
2766
|
+
+ (chain.asOf ? ` (last trade ${chain.asOf})` : "");
|
|
2767
|
+
const sessionWarning = refreshWarning ? null : priorSessionChainWarning(chain, identity.exchange, Date.now());
|
|
2768
|
+
// A venue that marks its contracts (Deribit) states IV and greeks without a quote.
|
|
2769
|
+
const marked = isMarkedChain(chain);
|
|
2770
|
+
const coin = chainCoinCurrency(chain);
|
|
2771
|
+
const modelWarning = !marked && inputs.spot == null && (chain.calls.length > 0 || chain.puts.length > 0)
|
|
2772
|
+
? `IV and delta need a current ${listing.key} quote; those columns are blank`
|
|
2773
|
+
: null;
|
|
2774
|
+
const listed = chain.expirationDates.length > 0 || chain.calls.length > 0 || chain.puts.length > 0;
|
|
2775
|
+
const warnings = [
|
|
2776
|
+
// FB's chain is the fund listed there now; a listing that stopped trading has none.
|
|
2777
|
+
...listingFateNotes(await fate(), { served: listed }),
|
|
2778
|
+
...await listingChoiceNotes(listing, market, quote, venues),
|
|
2779
|
+
...[refreshWarning ?? sessionWarning, modelWarning].filter((warning): warning is string => warning != null),
|
|
2780
|
+
];
|
|
2781
|
+
// Dated by the chain's last trade; a chain that failed to refresh is the stored one, stale.
|
|
2782
|
+
const freshness = rowsFreshness([], { asOf: chain.asOf ?? null }, {
|
|
2783
|
+
...(chain.dataSource ? { dataSource: chain.dataSource } : {}),
|
|
2784
|
+
...(chain.delayMinutes != null ? { delayMinutes: chain.delayMinutes } : {}),
|
|
2785
|
+
stale: refreshWarning != null,
|
|
2786
|
+
});
|
|
2787
|
+
const contractCount = chain.calls.length + chain.puts.length;
|
|
2788
|
+
ctx.printResult({
|
|
2789
|
+
data: chain,
|
|
2790
|
+
metadata: {
|
|
2791
|
+
...listingMetadata(identity),
|
|
2792
|
+
expirations: chain.expirationDates,
|
|
2793
|
+
currency,
|
|
2794
|
+
interval: OPTIONS_EXPORT_INTERVAL,
|
|
2795
|
+
// `iv` is annualised volatility and `delta` the option's change per 1.00 move in the underlying, both decimals.
|
|
2796
|
+
units: { iv: "annualized decimal", delta: "decimal per 1.00 move in the underlying" },
|
|
2797
|
+
// What each contract's `iv` and `impliedVolatility` are, so neither is read as the other.
|
|
2798
|
+
fields: marked ? MARKED_OPTION_FIELDS : OPTION_VOLATILITY_FIELDS,
|
|
2799
|
+
// What `iv` and `delta` on each contract are valued from; the provider's own impliedVolatility is left as sent.
|
|
2800
|
+
model: marked
|
|
2801
|
+
? { venue: chain.venue ?? null, marks: "venue", rate: chain.rate ?? 0, basis: "the expiry's forward", coin }
|
|
2802
|
+
: {
|
|
2803
|
+
spot: basisFields.spot, spotBasis: basisFields.spotBasis, spotAsOf: basisFields.spotAsOf,
|
|
2804
|
+
valuationTime: modelSpot ? new Date(modelSpot.valuationTime).toISOString() : null,
|
|
2805
|
+
dividendYield: inputs.dividendYield ?? null, rate: OPTION_MODEL_RATE,
|
|
2806
|
+
},
|
|
2807
|
+
openInterest: { asOf: basisFields.openInterestAsOf, listedAsOf: basis.openInterest?.listedDate ?? null,
|
|
2808
|
+
compared: basis.openInterest?.compared ?? 0, matched: basis.openInterest?.matched ?? 0 },
|
|
2809
|
+
},
|
|
2810
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
2811
|
+
freshness,
|
|
2812
|
+
}, {
|
|
2813
|
+
// Options a venue other than the US exchanges lists (Deribit's) are headed by that venue, not the coin's board.
|
|
2814
|
+
heading: chain.venue ? `${cliStyles.bold(identity.name || identity.symbol)}${cliStyles.muted(` · ${chain.venue}`)}` : listingHeading(identity),
|
|
2815
|
+
rows: (data: OptionsChain) => optionRows(data).map((row) => ({ ...row, ...basisFields })),
|
|
2816
|
+
// Choosing an expiry needs the list; once one is chosen the table is that expiry.
|
|
2817
|
+
summary: (data: OptionsChain) => expirationDate == null ? formatExpiryList(data.expirationDates) : "",
|
|
2818
|
+
empty: `No option contracts for ${listingTitle(identity)}.`,
|
|
2819
|
+
...(contractCount > 0 ? { footnote: optionsBasisFootnote(basis, chain) } : {}),
|
|
2820
|
+
exportIdentity: { symbolOrSeries: listing.key, currencyOrUnit: currency, interval: OPTIONS_EXPORT_INTERVAL },
|
|
2821
|
+
// Exports state each decimal's unit in its header and end every row with the spot and dates it is as of.
|
|
2822
|
+
columns: [
|
|
2823
|
+
{ key: "side", header: "Side" },
|
|
2824
|
+
{ key: "contract", header: "Contract" },
|
|
2825
|
+
{ key: "expiration", header: "Expiry" },
|
|
2826
|
+
{ key: "strike", header: "Strike" },
|
|
2827
|
+
{ key: "last", header: "Last" },
|
|
2828
|
+
{ key: "bid", header: "Bid" },
|
|
2829
|
+
{ key: "ask", header: "Ask" },
|
|
2830
|
+
{ key: "iv", header: "IV (annualized decimal)" },
|
|
2831
|
+
{ key: "ivSource", header: "IV source" },
|
|
2832
|
+
{ key: "delta", header: "Delta (decimal)" },
|
|
2833
|
+
{ key: "volume", header: "Vol" },
|
|
2834
|
+
{ key: "openInterest", header: "OI" },
|
|
2835
|
+
{ key: "spot", header: "Spot" },
|
|
2836
|
+
{ key: "spotBasis", header: "Spot basis" },
|
|
2837
|
+
{ key: "spotAsOf", header: "Spot as of" },
|
|
2838
|
+
{ key: "openInterestAsOf", header: "OI as of" },
|
|
2839
|
+
// A venue's marks and its coin prices (Deribit's BTC and ETH options).
|
|
2840
|
+
...(marked ? [
|
|
2841
|
+
{ key: "mark", header: "Mark (USD)" }, { key: "gamma", header: "Gamma (per 1.00)" },
|
|
2842
|
+
{ key: "vega", header: "Vega (USD per vol point)" }, { key: "theta", header: "Theta (USD per day)" },
|
|
2843
|
+
{ key: "forward", header: "Forward" }, { key: "expiresAt", header: "Settles" },
|
|
2844
|
+
] : []),
|
|
2845
|
+
...(coin ? [
|
|
2846
|
+
{ key: "coinBid", header: `Bid (${coin})` }, { key: "coinAsk", header: `Ask (${coin})` },
|
|
2847
|
+
{ key: "coinMark", header: `Mark (${coin})` }, { key: "coinLast", header: `Last (${coin})` },
|
|
2848
|
+
] : []),
|
|
2849
|
+
],
|
|
2850
|
+
// A coin's options are quoted in the coin: its bid, ask and mark, then the mark in dollars and the venue's greeks.
|
|
2851
|
+
textColumns: coin ? [
|
|
2852
|
+
{ key: "side", header: "Side" },
|
|
2853
|
+
{ key: "contract", header: "Contract", shrink: false },
|
|
2854
|
+
{ key: "strike", header: "Strike", align: "right" },
|
|
2855
|
+
{ key: "coinBid", header: `Bid ${coin}`, align: "right", format: formatCoinPremiumCell },
|
|
2856
|
+
{ key: "coinAsk", header: `Ask ${coin}`, align: "right", format: formatCoinPremiumCell },
|
|
2857
|
+
{ key: "coinMark", header: `Mark ${coin}`, align: "right", format: formatCoinPremiumCell },
|
|
2858
|
+
{ key: "mark", header: "Mark $", align: "right", format: formatOptionDollarCell },
|
|
2859
|
+
{ key: "iv", header: "IV", align: "right", format: (value, row) => formatOptionIvCell(value, row) },
|
|
2860
|
+
{ key: "delta", header: "Delta", align: "right", format: formatOptionDeltaCell },
|
|
2861
|
+
{ key: "gamma", header: "Gamma", align: "right", format: formatOptionGammaCell, optional: true, dropPriority: 3 },
|
|
2862
|
+
{ key: "vega", header: "Vega $", align: "right", format: formatOptionDollarCell, optional: true, dropPriority: 2 },
|
|
2863
|
+
{ key: "theta", header: "Theta $", align: "right", format: formatOptionDollarCell, optional: true, dropPriority: 2 },
|
|
2864
|
+
{ key: "volume", header: "Vol", align: "right", format: formatCountCell, optional: true, dropPriority: 1 },
|
|
2865
|
+
{ key: "openInterest", header: "OI", align: "right", format: formatCountCell, optional: true, dropPriority: 1 },
|
|
2866
|
+
] : [
|
|
2867
|
+
{ key: "side", header: "Side" },
|
|
2868
|
+
{ key: "contract", header: "Contract", shrink: false },
|
|
2869
|
+
// The contract symbol already carries the date, so this goes first when the table is narrow.
|
|
2870
|
+
{ key: "expiration", header: "Expiry", optional: true, dropPriority: 2 },
|
|
2871
|
+
{ key: "strike", header: "Strike", align: "right" },
|
|
2872
|
+
{ key: "last", header: "Last", align: "right" },
|
|
2873
|
+
{ key: "bid", header: "Bid", align: "right", format: (_value, row) => formatOptionQuoteCell(row, "bid") },
|
|
2874
|
+
{ key: "ask", header: "Ask", align: "right", format: (_value, row) => formatOptionQuoteCell(row, "ask") },
|
|
2875
|
+
{ key: "iv", header: "IV", align: "right", format: (value, row) => formatOptionIvCell(value, row) },
|
|
2876
|
+
{ key: "delta", header: "Delta", align: "right", format: formatOptionDeltaCell },
|
|
2877
|
+
{ key: "volume", header: "Vol", align: "right", format: formatCountCell, optional: true, dropPriority: 1 },
|
|
2878
|
+
{ key: "openInterest", header: "OI", align: "right", format: formatCountCell, optional: true, dropPriority: 1 },
|
|
2879
|
+
],
|
|
2880
|
+
});
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
/** What an options chain export's `interval` says: one quote snapshot, not bars. */
|
|
2885
|
+
const OPTIONS_EXPORT_INTERVAL = "snapshot";
|
|
2886
|
+
|
|
2887
|
+
/** `fx --date`: a past day as YYYY-MM-DD; today's close is not in yet, so it is refused, as is a day ahead. */
|
|
2888
|
+
function readFxDate(value: string, ctx: Parameters<CliCommandDef["execute"]>[1]): string {
|
|
2889
|
+
let date: string;
|
|
2890
|
+
try {
|
|
2891
|
+
date = isoDateOption(value, "date");
|
|
2892
|
+
} catch (error) {
|
|
2893
|
+
return ctx.fail(error instanceof Error ? error.message : String(error));
|
|
2894
|
+
}
|
|
2895
|
+
if (!date) return ctx.fail("--date takes a day, such as --date 2026-09-02.");
|
|
2896
|
+
const today = isoDateToday();
|
|
2897
|
+
if (date >= today) {
|
|
2898
|
+
return ctx.fail(`${date} has no daily close yet: --date takes a day before today (${today}).`, "Without --date, fx gives the latest rate.");
|
|
2899
|
+
}
|
|
2900
|
+
return date;
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
/**
|
|
2904
|
+
* `fx <pair> --date <day>`: the rate at that day's daily FX closes, read as
|
|
2905
|
+
* a position's purchase-date cost reads them (`fxCloseOnDate`): each
|
|
2906
|
+
* currency's USD pair at its close of the day, or of the last trading day
|
|
2907
|
+
* before it, which the answer then says, crossed through USD as the latest
|
|
2908
|
+
* rate is.
|
|
2909
|
+
*/
|
|
2910
|
+
async function runFxOnDate(
|
|
2911
|
+
parsed: Exclude<ReturnType<typeof parseFxArgs>, { error: string }>,
|
|
2912
|
+
date: string,
|
|
2913
|
+
args: readonly string[],
|
|
2914
|
+
market: MarketContext,
|
|
2915
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
2916
|
+
) {
|
|
2917
|
+
const { request, amount, toUsdByDefault } = parsed;
|
|
2918
|
+
const { currency, baseCurrency } = request;
|
|
2919
|
+
const pair = `${currency}/${baseCurrency}`;
|
|
2920
|
+
const history = market.fxHistory ?? apiClient;
|
|
2921
|
+
const codes = [...new Set([currency, baseCurrency].filter((code) => code !== "USD"))];
|
|
2922
|
+
const legs = new Map<string, FxCloseReading>([["USD", { rate: 1, date }]]);
|
|
2923
|
+
if (currency !== baseCurrency) {
|
|
2924
|
+
for (const code of codes) {
|
|
2925
|
+
const leg = dailyFxLeg(code);
|
|
2926
|
+
if (!leg) return ctx.fail(`${code} has no daily FX closes to read a rate for ${date} from.`);
|
|
2927
|
+
let reading: FxCloseReading | null;
|
|
2928
|
+
try {
|
|
2929
|
+
reading = await fxCloseOnDate(leg, date, history);
|
|
2930
|
+
} catch (error) {
|
|
2931
|
+
return ctx.fail(`The daily ${code} FX closes could not be read.`, error instanceof Error ? error.message : String(error));
|
|
2932
|
+
}
|
|
2933
|
+
if (!reading) {
|
|
2934
|
+
return ctx.fail(`No ${leg.invert ? `USD/${code}` : `${code}/USD`} daily close in the week up to ${date}.`,
|
|
2935
|
+
"Without --date, fx gives the latest rate.");
|
|
2936
|
+
}
|
|
2937
|
+
legs.set(code, reading);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
const from = legs.get(currency), into = legs.get(baseCurrency);
|
|
2941
|
+
const rate = currency === baseCurrency ? 1 : crossRate(from?.rate, into?.rate);
|
|
2942
|
+
// A cross is as old as its older close; a weekend or holiday reads the trading day before it.
|
|
2943
|
+
const closeDate = [...legs.values()].map((leg) => leg.date).sort()[0]!;
|
|
2944
|
+
const converted = amount == null ? null : amount * rate;
|
|
2945
|
+
const row = {
|
|
2946
|
+
currency, baseCurrency, rate, asOf: closeDate, stale: false, pair, inverse: 1 / rate, date,
|
|
2947
|
+
...(amount == null ? {} : { amount, converted }),
|
|
2948
|
+
};
|
|
2949
|
+
const warnings = [
|
|
2950
|
+
...request.readAs ?? [],
|
|
2951
|
+
...closeDate < date ? [`No daily close on ${date}, a weekend or market holiday: this is the close of ${closeDate}, the trading day before it.`] : [],
|
|
2952
|
+
...toUsdByDefault && amount != null
|
|
2953
|
+
? [`No target currency given, so ${currency} is converted to USD; for another, write fx ${args[0]!.trim()} ${currency}/<to> --date ${date}.`]
|
|
2954
|
+
: [],
|
|
2955
|
+
];
|
|
2956
|
+
ctx.printResult({
|
|
2957
|
+
data: [row],
|
|
2958
|
+
// A past close is what was asked for, never stale.
|
|
2959
|
+
freshness: rowsFreshness([], { status: "not-a-feed", basis: "daily FX close", asOf: closeDate }),
|
|
2960
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
2961
|
+
}, {
|
|
2962
|
+
text: () => amount == null ? describeFxRate(request, rate) : `${describeFxConversion(request, amount, rate)}\n${describeFxRate(request, rate)}`,
|
|
2963
|
+
exportIdentity: { symbolOrSeries: pair, currencyOrUnit: baseCurrency, interval: "1day" },
|
|
2964
|
+
columns: [
|
|
2965
|
+
{ key: "currency", header: "Currency" },
|
|
2966
|
+
{ key: "baseCurrency", header: "Base" },
|
|
2967
|
+
{ key: "rate", header: "Rate", align: "right" },
|
|
2968
|
+
{ key: "date", header: "Date" },
|
|
2969
|
+
{ key: "asOf", header: "Close" },
|
|
2970
|
+
{ key: "stale", header: "Stale" },
|
|
2971
|
+
{ key: "pair", header: "Pair" },
|
|
2972
|
+
{ key: "inverse", header: "Inverse", align: "right" },
|
|
2973
|
+
...(amount == null ? [] : [
|
|
2974
|
+
{ key: "amount", header: "Amount", align: "right" as const },
|
|
2975
|
+
{ key: "converted", header: "Converted", align: "right" as const },
|
|
2976
|
+
]),
|
|
2977
|
+
],
|
|
2978
|
+
});
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
2982
|
+
const args = [...rawArgs];
|
|
2983
|
+
const dateOption = takeOption(args, "--date");
|
|
2984
|
+
const forwardOption = takeOption(args, "--forward");
|
|
2985
|
+
if (args.length === 0) ctx.fail(`Usage: gloom ${FX_USAGE}`);
|
|
2986
|
+
// A forward is implied from today's spot and the latest short rates, which are all Gloom Cloud serves.
|
|
2987
|
+
if (dateOption != null && forwardOption != null) {
|
|
2988
|
+
ctx.fail(
|
|
2989
|
+
"fx --forward does not take --date: the forward is implied from today's spot and the latest short rates, and past short rates are not served.",
|
|
2990
|
+
`For that day's close: gloom fx ${args.join(" ")} --date ${dateOption}. For today's forward, drop --date.`,
|
|
2991
|
+
);
|
|
2992
|
+
}
|
|
2993
|
+
const date = dateOption == null ? null : readFxDate(dateOption, ctx);
|
|
2994
|
+
const tenor = forwardOption == null ? null : parseForwardTenor(forwardOption);
|
|
2995
|
+
if (tenor && "error" in tenor) ctx.fail(tenor.error, `Usage: gloom ${FX_FORWARD_USAGE}`);
|
|
2996
|
+
await withMarketData(ctx, async (market) => {
|
|
2997
|
+
const parsed = parseFxArgs(args, market.config.baseCurrency);
|
|
2998
|
+
if ("error" in parsed) return ctx.fail(parsed.error, `Usage: gloom ${forwardOption == null ? FX_USAGE : FX_FORWARD_USAGE}`);
|
|
2999
|
+
if (date) return runFxOnDate(parsed, date, args, market, ctx);
|
|
3000
|
+
const { request, amount, toUsdByDefault } = parsed;
|
|
3001
|
+
const { currency, baseCurrency } = request;
|
|
3002
|
+
const pair = `${currency}/${baseCurrency}`;
|
|
3003
|
+
if (tenor) {
|
|
3004
|
+
if (amount != null) ctx.fail("--forward prices a pair, not an amount.", `Usage: gloom ${FX_FORWARD_USAGE}`);
|
|
3005
|
+
if (currency === baseCurrency) ctx.fail(`${pair} has no forward: both sides are ${currency}.`, `Usage: gloom ${FX_FORWARD_USAGE}`);
|
|
3006
|
+
}
|
|
3007
|
+
// The short rates are read alongside spot. A failed read is held as a value, not left to reject
|
|
3008
|
+
// unobserved while spot loads, and is raised where the forward needs the rates.
|
|
3009
|
+
const shortRates = tenor && !("error" in tenor) ? apiClient.getCloudShortRates().then(
|
|
3010
|
+
(payload) => ({ payload }),
|
|
3011
|
+
(error: unknown) => ({ error: unavailableOnServer(error, "Forwards are not available from Gloom Cloud yet: it serves no short rates.") }),
|
|
3012
|
+
) : null;
|
|
3013
|
+
const reasons = new Map<string, string>();
|
|
3014
|
+
const load = (code: string) => market.dataProvider.getCachedQuery("getExchangeRate", [code])
|
|
3015
|
+
.load({ force: ctx.cliOptions.refresh }).catch((error) => {
|
|
3016
|
+
const reason = providerMissReason(error);
|
|
3017
|
+
if (reason) reasons.set(code, reason);
|
|
3018
|
+
return null;
|
|
3019
|
+
});
|
|
3020
|
+
// Every rate is a cross of two USD legs; a code priced in itself needs none.
|
|
3021
|
+
const codes = currency === baseCurrency ? [] : [currency, baseCurrency];
|
|
3022
|
+
const legs = await Promise.all(codes.map(load));
|
|
3023
|
+
const missing = codes.filter((_code, index) => !isUsableRate(legs[index]?.value));
|
|
3024
|
+
if (missing.length > 0) {
|
|
3025
|
+
// A reason the data service gave for the first missing leg says more than the generic line.
|
|
3026
|
+
const reason = missing.map((code) => reasons.get(code)).find(Boolean);
|
|
3027
|
+
if (reason) ctx.fail(reason);
|
|
3028
|
+
ctx.fail(
|
|
3029
|
+
`Exchange rate unavailable for ${missing.join(" and ")}.`,
|
|
3030
|
+
`${pair} is crossed from each currency's USD rate, and none came back for ${missing.join(" or ")}. Check the ISO code.`,
|
|
3031
|
+
);
|
|
3032
|
+
}
|
|
3033
|
+
const rate = codes.length === 0 ? 1 : crossRate(legs[0]?.value, legs[1]?.value);
|
|
3034
|
+
// A cross rate is only as current as its older leg.
|
|
3035
|
+
const observed = legs.flatMap((leg) => leg?.asOf ?? []);
|
|
3036
|
+
const asOf = observed.length > 0 ? new Date(Math.min(...observed)).toISOString() : null;
|
|
3037
|
+
const stale = legs.some((leg) => leg != null && (leg.staleAt <= Date.now() || leg.refreshError != null));
|
|
3038
|
+
const converted = amount == null ? null : amount * rate;
|
|
3039
|
+
const row = { currency, baseCurrency, rate, asOf, stale, pair, inverse: 1 / rate, ...(amount == null ? {} : { amount, converted }) };
|
|
3040
|
+
// The closing line dates the rate, and says when it is stale, as `fn FXC` does.
|
|
3041
|
+
const loaded = new Map(codes.flatMap((code, index) => {
|
|
3042
|
+
const leg = legs[index];
|
|
3043
|
+
return leg ? [[code, {
|
|
3044
|
+
rate: leg.value,
|
|
3045
|
+
timing: {
|
|
3046
|
+
asOf: leg.asOf, staleAt: leg.staleAt, error: leg.refreshError, delayMinutes: leg.delayMinutes,
|
|
3047
|
+
intradayBar: leg.intradayBar, staleReason: leg.staleReason,
|
|
3048
|
+
},
|
|
3049
|
+
}] as const] : [];
|
|
3050
|
+
}));
|
|
3051
|
+
const freshness = fxPairFreshness(loaded);
|
|
3052
|
+
// A rate that is its pair's bar says which bar, and a stale one why it stands.
|
|
3053
|
+
const rateNotes = fxRateNotes(new Map([...loaded].map(([code, leg]) => [code, leg.timing])));
|
|
3054
|
+
if (tenor && shortRates && !("error" in tenor)) {
|
|
3055
|
+
const read = await shortRates;
|
|
3056
|
+
if ("error" in read) throw read.error;
|
|
3057
|
+
const { rates } = read.payload;
|
|
3058
|
+
// BASE/QUOTE: the forward needs the rate of each; a missing one is named, never filled in.
|
|
3059
|
+
const base = shortRateFor(currency, rates);
|
|
3060
|
+
const quote = shortRateFor(baseCurrency, rates);
|
|
3061
|
+
const missing = [base, quote].flatMap((lookup) => "error" in lookup ? [lookup.error] : []);
|
|
3062
|
+
if ("error" in base || "error" in quote) {
|
|
3063
|
+
return ctx.fail(`No ${tenor.label} forward for ${pair}. ${missing.join(" ")}`, "The forward is implied from both currencies' short rates by covered interest parity.");
|
|
3064
|
+
}
|
|
3065
|
+
const implied = impliedForward(rate, tenor.days, base.rate, quote.rate);
|
|
3066
|
+
const forwardDate = new Date(Math.floor(Date.now() / 86_400_000) * 86_400_000 + tenor.days * 86_400_000).toISOString().slice(0, 10);
|
|
3067
|
+
const rateFields = (short: typeof base.rate) => ({
|
|
3068
|
+
currency: short.currency, name: short.name, tenor: short.tenor, value: short.value, asOf: short.asOf,
|
|
3069
|
+
dayCountBasis: short.dayCountBasis, publisher: short.publisher, sourceUrl: short.sourceUrl, stale: short.stale,
|
|
3070
|
+
});
|
|
3071
|
+
const staleRates = [base.rate, quote.rate].filter((short) => short.stale)
|
|
3072
|
+
.map((short) => `${short.name} (${short.currency}) is the last copy read, dated ${short.asOf ?? "undated"}; its source could not be read since.`);
|
|
3073
|
+
const forwardRow = {
|
|
3074
|
+
pair, tenor: tenor.label, days: tenor.days, forwardDate, spot: rate, spotAsOf: asOf, forward: implied.forward,
|
|
3075
|
+
points: implied.points, pointSize: implied.pointSize, premiumPercent: implied.premiumPercent, annualizedPercent: implied.annualizedPercent,
|
|
3076
|
+
baseRate: rateFields(base.rate), quoteRate: rateFields(quote.rate), method: "covered interest parity", label: IMPLIED_FORWARD_LABEL,
|
|
3077
|
+
};
|
|
3078
|
+
const growth = (short: typeof base.rate) => `(1 + ${formatRatePercent(short.value)} × ${tenor.days}/${short.dayCountBasis})`;
|
|
3079
|
+
// A currency typed as its name (yen) says which code it was read as, as the spot answer does.
|
|
3080
|
+
const forwardWarnings = [...request.readAs ?? [], ...rateNotes, ...staleRates];
|
|
3081
|
+
return ctx.printResult({ data: [forwardRow], freshness, ...(forwardWarnings.length > 0 ? { warnings: forwardWarnings } : {}) }, {
|
|
3082
|
+
text: () => [
|
|
3083
|
+
`${pair} ${tenor.label} forward ${formatFxRate(implied.forward)}, ${IMPLIED_FORWARD_LABEL}`,
|
|
3084
|
+
`Spot ${formatFxRate(rate)} · forward points ${formatForwardPoints(implied.points)} (pips of ${implied.pointSize}) · ${implied.premiumPercent >= 0 ? "premium" : "discount"} ${formatSignedPercent(implied.premiumPercent)} over ${tenor.days} days to ${forwardDate}, ${formatSignedPercent(implied.annualizedPercent, 2)} a year (×360/${tenor.days})`,
|
|
3085
|
+
`${currency} rate: ${describeShortRate(base.rate)}`,
|
|
3086
|
+
`${baseCurrency} rate: ${describeShortRate(quote.rate)}`,
|
|
3087
|
+
`Covered interest parity: ${formatFxRate(rate)} × ${growth(quote.rate)} / ${growth(base.rate)}; each rate earns simple interest over the ${tenor.days} days, standing in for a ${tenor.label} term rate.`,
|
|
3088
|
+
].join("\n"),
|
|
3089
|
+
exportIdentity: { symbolOrSeries: pair, currencyOrUnit: baseCurrency, interval: `forward ${tenor.label}` },
|
|
3090
|
+
columns: [
|
|
3091
|
+
{ key: "pair", header: "Pair" },
|
|
3092
|
+
{ key: "tenor", header: "Tenor" },
|
|
3093
|
+
{ key: "days", header: "Days", align: "right" },
|
|
3094
|
+
{ key: "forwardDate", header: "Forward date" },
|
|
3095
|
+
{ key: "spot", header: "Spot", align: "right" },
|
|
3096
|
+
{ key: "forward", header: "Forward", align: "right" },
|
|
3097
|
+
{ key: "points", header: "Points", align: "right" },
|
|
3098
|
+
{ key: "premiumPercent", header: "Premium %", align: "right" },
|
|
3099
|
+
{ key: "annualizedPercent", header: "Annualized %", align: "right" },
|
|
3100
|
+
],
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
// An amount with one currency names no target; the answer says it went to USD.
|
|
3104
|
+
const warnings = [
|
|
3105
|
+
// A currency typed as its name (yen) says which code it was read as.
|
|
3106
|
+
...request.readAs ?? [],
|
|
3107
|
+
...toUsdByDefault && amount != null
|
|
3108
|
+
? [`No target currency given, so ${currency} is converted to USD; for another, write fx ${args[0]!.trim()} ${currency}/<to>.`]
|
|
3109
|
+
: [],
|
|
3110
|
+
...rateNotes,
|
|
3111
|
+
];
|
|
3112
|
+
// The peso's dollar rate is the official one; the market rates Buenos Aires prices imply follow it, each with its inputs.
|
|
3113
|
+
const peso = isPesoDollarPair(currency, baseCurrency)
|
|
3114
|
+
? await loadPesoDollarRates((targets) => market.dataProvider.getQuotesBatch(targets, { forceRefresh: ctx.cliOptions.refresh }))
|
|
3115
|
+
: null;
|
|
3116
|
+
if (peso && peso.missing.length > 0) warnings.push(`No price for ${peso.missing.join(", ")}, so the rates from ${peso.missing.length === 1 ? "it are" : "them are"} left out.`);
|
|
3117
|
+
const rateText = peso ? `${describeFxRate(request, rate)} · ${peso.spotKind} rate` : describeFxRate(request, rate);
|
|
3118
|
+
ctx.printResult({ data: [peso ? { ...row, ...pesoDollarRatesJson(peso) } : row], freshness, warnings: warnings.length > 0 ? warnings : undefined }, {
|
|
3119
|
+
// The conversion, then the rate it used; the source line under it dates that rate.
|
|
3120
|
+
text: () => [
|
|
3121
|
+
...amount == null ? [] : [describeFxConversion(request, amount, rate)],
|
|
3122
|
+
rateText,
|
|
3123
|
+
...peso ? pesoDollarRateLines(peso) : [],
|
|
3124
|
+
].join("\n"),
|
|
3125
|
+
// The rate is the base currency's price of one unit of the other, at the latest quote of each leg.
|
|
3126
|
+
exportIdentity: { symbolOrSeries: pair, currencyOrUnit: baseCurrency, interval: "spot" },
|
|
3127
|
+
columns: [
|
|
3128
|
+
{ key: "currency", header: "Currency" },
|
|
3129
|
+
{ key: "baseCurrency", header: "Base" },
|
|
3130
|
+
{ key: "rate", header: "Rate", align: "right" },
|
|
3131
|
+
{ key: "asOf", header: "As Of" },
|
|
3132
|
+
{ key: "stale", header: "Stale" },
|
|
3133
|
+
{ key: "pair", header: "Pair" },
|
|
3134
|
+
{ key: "inverse", header: "Inverse", align: "right" },
|
|
3135
|
+
...(amount == null ? [] : [
|
|
3136
|
+
{ key: "amount", header: "Amount", align: "right" as const },
|
|
3137
|
+
{ key: "converted", header: "Converted", align: "right" as const },
|
|
3138
|
+
]),
|
|
3139
|
+
],
|
|
3140
|
+
});
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
async function runEarnings(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
3145
|
+
const args = [...rawArgs];
|
|
3146
|
+
const exchangeOption = takeOption(args, "--exchange");
|
|
3147
|
+
const entries = normalizeSymbols(args);
|
|
3148
|
+
if (entries.length === 0) ctx.fail("Usage: gloom earnings <symbol...>");
|
|
3149
|
+
await withCliServices(ctx, async (services) => {
|
|
3150
|
+
const symbols = await readSymbols(entries, services.dataProvider);
|
|
3151
|
+
const listings = await Promise.all(symbols.map((symbol) => requireCliListing(
|
|
3152
|
+
symbol, exchangeOption, services, ctx, { ownExchangeWins: symbols.length > 1 },
|
|
3153
|
+
)));
|
|
3154
|
+
const keys = listings.map((listing) => listing.key);
|
|
3155
|
+
// Each bare symbol's venues cost a search, so a long list (a portfolio check, not a lookup) names none.
|
|
3156
|
+
const venues = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
|
|
3157
|
+
: listings.map((listing) => [listing, loadBareVenues(listing, services)] as const));
|
|
3158
|
+
let calendar: EarningsEvent[];
|
|
3159
|
+
try {
|
|
3160
|
+
calendar = await loadForListing(
|
|
3161
|
+
listings, services, ctx,
|
|
3162
|
+
() => services.dataProvider.getEarningsCalendar(keys),
|
|
3163
|
+
(found) => found.length === 0,
|
|
3164
|
+
);
|
|
3165
|
+
} catch (error) {
|
|
3166
|
+
const failure = earningsCalendarFailure(error, keys);
|
|
3167
|
+
if (failure) ctx.fail(failure);
|
|
3168
|
+
throw error;
|
|
3169
|
+
}
|
|
3170
|
+
// Each date is resolved as events, EE, ERN and OSA resolve it: one that has come is read against the
|
|
3171
|
+
// company's quarters on record, for its actual and the next report on record (shared/earnings-calendar).
|
|
3172
|
+
const today = newYorkDay();
|
|
3173
|
+
const resolved = await resolveListingEvents(services.dataProvider, listings.map((listing) => ({
|
|
3174
|
+
key: listing.key, symbol: listing.request.symbol, exchange: listing.request.exchange,
|
|
3175
|
+
})), calendar, today);
|
|
3176
|
+
const events: ResolvedEarningsEvent[] = calendar
|
|
3177
|
+
.map((event) => resolved.get(event.symbol.toUpperCase()) ?? { ...event, dateStatus: calendarStatus(event, today) })
|
|
3178
|
+
.sort((left, right) => left.earningsDate.getTime() - right.earningsDate.getTime());
|
|
3179
|
+
const eventOf = (listing: CliListing) => events.find((event) => event.symbol.toUpperCase() === listing.key.toUpperCase());
|
|
3180
|
+
const [abroadNotes, choiceNotes] = await Promise.all([
|
|
3181
|
+
// A listing abroad with no date of its own says so, with its US line's date when that line is the same company.
|
|
3182
|
+
Promise.all(listings.filter((listing) => isListingAbroad(listing) && !eventOf(listing))
|
|
3183
|
+
.map((listing) => noListingCalendarNote(listing, services))),
|
|
3184
|
+
Promise.all(listings.map(async (listing) => {
|
|
3185
|
+
const listed = venues.get(listing);
|
|
3186
|
+
return listed ? earningsChoiceNote(listing, eventOf(listing), await listed) : null;
|
|
3187
|
+
})),
|
|
3188
|
+
]);
|
|
3189
|
+
const noCalendar = abroadNotes.join("\n");
|
|
3190
|
+
// Text prints the listings without a calendar under the table; other formats carry them as warnings.
|
|
3191
|
+
const warnings = [
|
|
3192
|
+
...listings.flatMap(listingReadNotes),
|
|
3193
|
+
...choiceNotes.filter((note): note is string => !!note),
|
|
3194
|
+
...ctx.cliOptions.format === "text" ? [] : abroadNotes,
|
|
3195
|
+
];
|
|
3196
|
+
ctx.printResult({
|
|
3197
|
+
data: events,
|
|
3198
|
+
// Dated by when it was read: a row's date is when the report is, not how old the calendar is.
|
|
3199
|
+
freshness: rowsFreshness([], { status: "not-a-feed", basis: "calendar", oldest: null }),
|
|
3200
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
3201
|
+
}, {
|
|
3202
|
+
rows: earningsRows,
|
|
3203
|
+
columns: [
|
|
3204
|
+
{ key: "date", header: "Date" },
|
|
3205
|
+
{ key: "status", header: "Status" },
|
|
3206
|
+
{ key: "symbol", header: "Symbol" },
|
|
3207
|
+
{ key: "name", header: "Name" },
|
|
3208
|
+
{ key: "timing", header: "Timing" },
|
|
3209
|
+
{ key: "epsEstimate", header: "EPS Est", align: "right", format: formatEpsCell },
|
|
3210
|
+
{ key: "epsActual", header: "EPS", align: "right", format: formatEpsCell },
|
|
3211
|
+
],
|
|
3212
|
+
...(noCalendar ? { empty: noCalendar, footnote: noCalendar } : {}),
|
|
3213
|
+
});
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
3218
|
+
{
|
|
3219
|
+
name: "quote",
|
|
3220
|
+
description: "Show the latest price for one or more symbols",
|
|
3221
|
+
help: {
|
|
3222
|
+
group: CLI_COMMAND_GROUPS.research,
|
|
3223
|
+
usage: ["quote <symbol...>", "quote --file <path>"],
|
|
3224
|
+
options: [EXCHANGE_OPTION, LIST_FILE_OPTION],
|
|
3225
|
+
sections: [QUOTE_CSV_SECTION],
|
|
3226
|
+
examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote binance:BZUSDT", "quote AAPL --json", "quote --file holdings.csv",
|
|
3227
|
+
"quote --file holdings.csv --csv > quotes.csv"],
|
|
3228
|
+
},
|
|
3229
|
+
execute: (args, ctx) => runQuote(args, ctx, "quote"),
|
|
3230
|
+
},
|
|
3231
|
+
{
|
|
3232
|
+
name: "compare",
|
|
3233
|
+
description: "Compare quotes for several symbols side by side",
|
|
3234
|
+
help: {
|
|
3235
|
+
group: CLI_COMMAND_GROUPS.research,
|
|
3236
|
+
usage: ["compare <symbol...>", "compare --file <path>"],
|
|
3237
|
+
options: [EXCHANGE_OPTION, LIST_FILE_OPTION],
|
|
3238
|
+
sections: [QUOTE_CSV_SECTION],
|
|
3239
|
+
examples: ["compare KO PEP", "compare SAN:EPA SAN:NYSE", "compare SPY QQQ IWM --csv", "compare - < peers.txt"],
|
|
3240
|
+
},
|
|
3241
|
+
execute: (args, ctx) => runQuote(args, ctx, "compare"),
|
|
3242
|
+
},
|
|
3243
|
+
{
|
|
3244
|
+
name: "history",
|
|
3245
|
+
description: "Fetch open, high, low, close, and volume over a range",
|
|
3246
|
+
help: {
|
|
3247
|
+
group: CLI_COMMAND_GROUPS.research,
|
|
3248
|
+
usage: [HISTORY_USAGE, "history --file <path> [--range <range> | --from <year|date> [--to <year|date>]]"],
|
|
3249
|
+
options: [
|
|
3250
|
+
{ flags: "--range <range>", description: `${HISTORY_RANGES.join(", ")} (default 1Y); ${HISTORY_YTD_RANGE}; ${HISTORY_ALL_RANGE}` },
|
|
3251
|
+
{ flags: "--from <year|date>, --start <year|date>", description: `${PRICE_WINDOW_FROM_HELP}. Up to five years come in daily bars, up to twenty weekly, longer monthly` },
|
|
3252
|
+
{ flags: "--to <year|date>, --end <year|date>", description: PRICE_WINDOW_TO_HELP },
|
|
3253
|
+
{ flags: "--file <path>", description: "Read the symbols from a text or CSV file, one per line (- reads standard input), and write every symbol's bars in one table" },
|
|
3254
|
+
EXCHANGE_OPTION,
|
|
3255
|
+
],
|
|
3256
|
+
sections: [{
|
|
3257
|
+
title: "Bars",
|
|
3258
|
+
lines: [
|
|
3259
|
+
`Each range comes in one bar size: ${historyIntervalsByRange()}. The line under the heading, and the --json metadata, give the currency, the bar size actually served, the first and last bar and the bar count. A warning says when the bars are coarser, or start later, than the range asked for, and for a coin, which trades every day, names each day it has no bar for.`,
|
|
3260
|
+
"A bar whose prices contradict each other (high below the open or close, or low above them) is left blank and flagged, such as high<open, with the count in a warning. --json rows carry currency, unit, interval and flag.",
|
|
3261
|
+
"A break in the series flags the bar after it, which keeps its prices: gap after more than three months with no bar (weeks for weekly, daily and intraday bars), jump when the close is over 100 times the one before or under a hundredth of it. A warning names each break, and the heading says the date the series runs unbroken from.",
|
|
3262
|
+
"The heading's last line is the max drawdown of the bars shown: the largest fall from a close to a later close, with the peak and the trough, their dates, and the date a close got back to the peak, if one did (maxDrawdown in --json).",
|
|
3263
|
+
],
|
|
3264
|
+
}, {
|
|
3265
|
+
title: "CSV",
|
|
3266
|
+
lines: [
|
|
3267
|
+
"Every --csv has one header, whatever the symbol, range or market state: Date, Open, High, Low, Close, Volume, Flag, Partial, Filled, Unsettled, Volume unit, Volume note, Venue volume, Volume venue, status, then symbol_or_series, currency_or_unit, interval, source and as_of. Prices are plain numbers in the currency currency_or_unit names. Partial, Filled and Unsettled are true or false, and status says why a row has no prices.",
|
|
3268
|
+
],
|
|
3269
|
+
}, {
|
|
3270
|
+
title: "Several symbols",
|
|
3271
|
+
lines: [
|
|
3272
|
+
`history AAPL MSFT 000660.KS, or --file with one symbol a line (- reads standard input), writes every symbol's bars in one long table, ${HISTORY_LIST_CONCURRENCY} symbols loading at once. Each row names its symbol, its currency and the as-of of that symbol's bars in symbol_or_series, currency_or_unit and as_of.`,
|
|
3273
|
+
"A symbol with no history is one row with its reason in status, and a warning names it; the command fails only when no symbol has any. --tail 5 keeps each symbol's newest five bars.",
|
|
3274
|
+
],
|
|
3275
|
+
}, {
|
|
3276
|
+
title: "Long history",
|
|
3277
|
+
lines: [
|
|
3278
|
+
"ZAR has daily FRED data from 1980: gloom fred DEXSFUS --start 1980 --sort asc --csv",
|
|
3279
|
+
"NGN, SAR and ILS have no FRED series. Their daily bars cover the last five years (gloom fn G USDNGN=X --range-preset 5Y --chart-resolution 1d --csv); before that there are monthly bars only (gloom history USDNGN=X --range ALL).",
|
|
3280
|
+
],
|
|
3281
|
+
}],
|
|
3282
|
+
examples: [
|
|
3283
|
+
"history AAPL", "history BHP:ASX --range 5Y", "history JPM --from 2007 --to 2009", "history ZAR=X --range ALL", "history ZAR=X --tail 5",
|
|
3284
|
+
"history AAPL --range 5Y --csv > aapl.csv", "history AAPL MSFT 000660.KS --range 1M --csv",
|
|
3285
|
+
"history --file holdings.txt --start 2024-01-01 --end 2024-12-31 --csv > bars.csv",
|
|
3286
|
+
],
|
|
3287
|
+
},
|
|
3288
|
+
unknownOptionHint: historyOptionHint,
|
|
3289
|
+
execute: runHistory,
|
|
3290
|
+
},
|
|
3291
|
+
{
|
|
3292
|
+
name: "options",
|
|
3293
|
+
description: "Fetch an options chain, or rank LEAPS across symbols",
|
|
3294
|
+
help: {
|
|
3295
|
+
group: CLI_COMMAND_GROUPS.research,
|
|
3296
|
+
usage: [OPTIONS_USAGE, LEAPS_USAGE],
|
|
3297
|
+
options: [
|
|
3298
|
+
{
|
|
3299
|
+
flags: `--expiration <${OPTION_EXPIRATION_PLACEHOLDER}>, --expiry <${OPTION_EXPIRATION_PLACEHOLDER}>`,
|
|
3300
|
+
description: "Expiration as a date (2028-01-21) or Unix seconds; defaults to the nearest one, and lists the others. With --leaps: rank that expiry alone",
|
|
3301
|
+
},
|
|
3302
|
+
EXCHANGE_OPTION,
|
|
3303
|
+
{ flags: "--leaps", description: "Rank long-dated contracts across the symbols given, as a stock replacement; more than a year out unless a window says otherwise" },
|
|
3304
|
+
{ flags: "--side <calls|puts>", description: `With --leaps: the side to rank (default ${DEFAULT_LEAPS_CRITERIA.side}s)` },
|
|
3305
|
+
{
|
|
3306
|
+
flags: "--delta <low-high>",
|
|
3307
|
+
description: `With --leaps: absolute delta band (default ${DEFAULT_LEAPS_CRITERIA.minDelta.toFixed(2)}-${DEFAULT_LEAPS_CRITERIA.maxDelta.toFixed(2)})`,
|
|
3308
|
+
},
|
|
3309
|
+
{
|
|
3310
|
+
flags: "--max-spread <percent>",
|
|
3311
|
+
description: `With --leaps: widest bid/ask spread, in percent of the midpoint (default ${DEFAULT_LEAPS_CRITERIA.maxSpreadPercent})`,
|
|
3312
|
+
},
|
|
3313
|
+
{ flags: "--min-oi <contracts>", description: `With --leaps: least open interest (default ${DEFAULT_LEAPS_CRITERIA.minOpenInterest})` },
|
|
3314
|
+
{
|
|
3315
|
+
flags: "--min-days <days>",
|
|
3316
|
+
description: `With --leaps: fewest calendar days to expiry (default ${DEFAULT_LEAPS_CRITERIA.minDays}); 180 reaches six months out`,
|
|
3317
|
+
},
|
|
3318
|
+
{ flags: "--max-days <days>", description: "With --leaps: most calendar days to expiry; --min-days 365 --max-days 730 is 12 to 24 months" },
|
|
3319
|
+
{ flags: "--expiry-from <date>", description: `With --leaps: first expiry date kept, as ${DATE_OPTION_EXAMPLES}; replaces the one-year floor` },
|
|
3320
|
+
{ flags: "--expiry-to <date>", description: "With --leaps: last expiry date kept" },
|
|
3321
|
+
{
|
|
3322
|
+
flags: "--columns <list>",
|
|
3323
|
+
description: `With --leaps: add figures, comma-separated: ${LEAPS_EXTRA_COLUMNS.join(", ")}`,
|
|
3324
|
+
},
|
|
3325
|
+
{
|
|
3326
|
+
flags: "--sort <order>",
|
|
3327
|
+
description: "With --leaps: carry (extrinsic per year, lowest first; the default), spread, oi, delta, expiry or symbol",
|
|
3328
|
+
},
|
|
3329
|
+
],
|
|
3330
|
+
sections: [{
|
|
3331
|
+
title: "LEAPS",
|
|
3332
|
+
lines: [
|
|
3333
|
+
"--leaps reads each symbol's expiries in the window and keeps the liquid contracts in the delta band, ranked by extrinsic per year: time value (midpoint less intrinsic) as a percent of spot, per year to expiry.",
|
|
3334
|
+
"--min-days, --max-days, --expiry-from and --expiry-to set the window together; without a lower bound it starts a year out. Each symbol reads at most 8 expiries, the ones nearest the window's start, and a warning names any it left.",
|
|
3335
|
+
"--columns adds iv (the contract's implied vol), ivr (the underlying's IV rank), cost (midpoint x 100 per contract), breakeven (at the midpoint, and its distance from spot), next-earnings, ex-dividend and carry-net "
|
|
3336
|
+
+ `(extrinsic per year net of the ${Number((OPTION_MODEL_RATE * 100).toFixed(2))}% rate on the strike and the dividend yield). A figure a symbol lacks is blank, with a warning.`,
|
|
3337
|
+
"A symbol without LEAPS, a quote or a chain is named in a warning and the others still rank. docs/research-data.md defines each figure.",
|
|
3338
|
+
],
|
|
3339
|
+
}],
|
|
3340
|
+
examples: [
|
|
3341
|
+
"options AAPL",
|
|
3342
|
+
"options AAPL --expiration 2028-01-21",
|
|
3343
|
+
"options AAPL:NASDAQ --json",
|
|
3344
|
+
"options AAPL MSFT NVDA GOOGL AMZN META --leaps",
|
|
3345
|
+
"options SPY QQQ --leaps --side puts --delta 0.20-0.40 --csv",
|
|
3346
|
+
"options MSFT --leaps --min-days 365 --max-days 730",
|
|
3347
|
+
"options AAPL MSFT NVDA --leaps --min-days 180 --columns iv,ivr,cost,breakeven,carry-net",
|
|
3348
|
+
],
|
|
3349
|
+
},
|
|
3350
|
+
execute: runOptions,
|
|
3351
|
+
},
|
|
3352
|
+
{
|
|
3353
|
+
name: "provider-search",
|
|
3354
|
+
description: "Search instruments at the connected data providers",
|
|
3355
|
+
help: {
|
|
3356
|
+
group: CLI_COMMAND_GROUPS.research,
|
|
3357
|
+
usage: ["provider-search <query>"],
|
|
3358
|
+
examples: ["provider-search toyota"],
|
|
3359
|
+
},
|
|
3360
|
+
execute: async (args, ctx) => {
|
|
3361
|
+
const query = args.join(" ");
|
|
3362
|
+
if (!query) ctx.fail("Usage: gloom provider-search <query>");
|
|
3363
|
+
await withMarketData(ctx, async (market) => {
|
|
3364
|
+
const results = await market.dataProvider.search(query);
|
|
3365
|
+
ctx.printResult({ data: results.slice(0, ctx.cliOptions.limit ?? results.length) }, {
|
|
3366
|
+
textColumns: [
|
|
3367
|
+
{ key: "symbol", header: "Symbol" },
|
|
3368
|
+
{ key: "name", header: "Name" },
|
|
3369
|
+
{ key: "exchange", header: "Exchange" },
|
|
3370
|
+
{ key: "type", header: "Type" },
|
|
3371
|
+
{ key: "currency", header: "Currency" },
|
|
3372
|
+
{ key: "providerId", header: "Provider" },
|
|
3373
|
+
],
|
|
3374
|
+
empty: `No instruments match "${query}".`,
|
|
3375
|
+
});
|
|
3376
|
+
});
|
|
3377
|
+
},
|
|
3378
|
+
},
|
|
3379
|
+
{
|
|
3380
|
+
name: "financials",
|
|
3381
|
+
description: "Fetch annual revenue, profit, and EPS",
|
|
3382
|
+
help: {
|
|
3383
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3384
|
+
usage: ["financials <symbol>"],
|
|
3385
|
+
options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION],
|
|
3386
|
+
examples: ["financials MSFT", "financials SAN:EPA", "financials MSFT --json", "financials MSFT --csv > msft.csv"],
|
|
3387
|
+
},
|
|
3388
|
+
unknownOptionHint: financialsOptionHint,
|
|
3389
|
+
execute: (args, ctx) => runFinancials(args, ctx, "statements"),
|
|
3390
|
+
},
|
|
3391
|
+
{
|
|
3392
|
+
name: "fundamentals",
|
|
3393
|
+
description: "Fetch fundamentals and the company profile",
|
|
3394
|
+
help: {
|
|
3395
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3396
|
+
usage: ["fundamentals <symbol>"],
|
|
3397
|
+
options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION, ...TAX_RATE_HELP_OPTIONS],
|
|
3398
|
+
examples: ["fundamentals NVDA", "fundamentals ASML:AMS", "fundamentals NVDA --csv --section fundamentals", "fundamentals MUB --tax-rate 40.8",
|
|
3399
|
+
"fundamentals CMF --tax-rate 40.8 --state-rate 13.3 --state CA"],
|
|
3400
|
+
},
|
|
3401
|
+
execute: (args, ctx) => runFinancials(args, ctx, "fundamentals"),
|
|
3402
|
+
},
|
|
3403
|
+
{
|
|
3404
|
+
name: "valuation",
|
|
3405
|
+
description: "Fetch market cap, enterprise value, and valuation multiples",
|
|
3406
|
+
help: {
|
|
3407
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3408
|
+
usage: ["valuation <symbol>"],
|
|
3409
|
+
options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION],
|
|
3410
|
+
examples: ["valuation NVDA", "valuation BP:LSE", "valuation NVDA --csv"],
|
|
3411
|
+
},
|
|
3412
|
+
execute: (args, ctx) => runFinancials(args, ctx, "valuation"),
|
|
3413
|
+
},
|
|
3414
|
+
{
|
|
3415
|
+
name: "earnings",
|
|
3416
|
+
description: "Show upcoming and recent earnings dates",
|
|
3417
|
+
help: {
|
|
3418
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3419
|
+
usage: ["earnings <symbol...>"],
|
|
3420
|
+
options: [EXCHANGE_OPTION],
|
|
3421
|
+
examples: ["earnings AAPL MSFT GOOGL", "earnings SAN:EPA"],
|
|
3422
|
+
},
|
|
3423
|
+
execute: runEarnings,
|
|
3424
|
+
},
|
|
3425
|
+
{
|
|
3426
|
+
name: "events",
|
|
3427
|
+
description: "Fetch dividends, splits, and earnings events",
|
|
3428
|
+
help: {
|
|
3429
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3430
|
+
usage: ["events <symbol>"],
|
|
3431
|
+
options: [EXCHANGE_OPTION],
|
|
3432
|
+
examples: ["events KO", "events BHP:ASX"],
|
|
3433
|
+
},
|
|
3434
|
+
execute: runEvents,
|
|
3435
|
+
},
|
|
3436
|
+
{
|
|
3437
|
+
name: "analyst",
|
|
3438
|
+
description: "Fetch analyst rating changes and price targets",
|
|
3439
|
+
help: {
|
|
3440
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3441
|
+
usage: ["analyst <symbol>"],
|
|
3442
|
+
options: [EXCHANGE_OPTION],
|
|
3443
|
+
examples: ["analyst TSLA", "analyst SAN:EPA"],
|
|
3444
|
+
},
|
|
3445
|
+
execute: runAnalyst,
|
|
3446
|
+
},
|
|
3447
|
+
{
|
|
3448
|
+
name: "holders",
|
|
3449
|
+
description: "Fetch institutional holders, or the 13D/13G beneficial owners",
|
|
3450
|
+
help: {
|
|
3451
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3452
|
+
usage: ["holders <symbol> [--form 13f|13d|13g|all] [--history]"],
|
|
3453
|
+
options: [
|
|
3454
|
+
{ flags: "--form <form>", description: "13f for the holder table (default); 13d, 13g or all for beneficial owners over 5%" },
|
|
3455
|
+
{ flags: "--history", description: "With --form 13d, 13g or all, every report newest first instead of the latest per filer" },
|
|
3456
|
+
EXCHANGE_OPTION,
|
|
3457
|
+
],
|
|
3458
|
+
sections: [{
|
|
3459
|
+
title: "Output",
|
|
3460
|
+
lines: [
|
|
3461
|
+
"Holders run largest value first, as in fn HDS. --csv and --json carry the HDS report's columns: values in the currency the header names, shares, and the quarter's change and the stake in percent (0.59 is 0.59%).",
|
|
3462
|
+
"A US listing lists every 13F filer, as fn 13F does: the largest 25, or as many as --limit asks up to 200; --tail counts back from the end of those, not from the smallest filer. A listing abroad has only the source's largest few.",
|
|
3463
|
+
],
|
|
3464
|
+
}],
|
|
3465
|
+
examples: ["holders AAPL", "holders SAN:EPA", "holders SAN --csv", "holders CAR --form all", "holders CAR --form 13g --history --json"],
|
|
3466
|
+
},
|
|
3467
|
+
execute: (args, ctx) => runHolders(args, ctx, "holders"),
|
|
3468
|
+
},
|
|
3469
|
+
{
|
|
3470
|
+
name: "insider",
|
|
3471
|
+
description: "Fetch insider holders",
|
|
3472
|
+
help: {
|
|
3473
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3474
|
+
usage: ["insider <symbol>"],
|
|
3475
|
+
options: [EXCHANGE_OPTION],
|
|
3476
|
+
examples: ["insider NVDA", "insider BP:LSE"],
|
|
3477
|
+
},
|
|
3478
|
+
execute: (args, ctx) => runHolders(args, ctx, "insider", new Set(["insider", "direct"])),
|
|
3479
|
+
},
|
|
3480
|
+
{
|
|
3481
|
+
name: "13f",
|
|
3482
|
+
description: "Fetch institutional and fund holders",
|
|
3483
|
+
help: {
|
|
3484
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3485
|
+
usage: ["13f <symbol>"],
|
|
3486
|
+
options: [EXCHANGE_OPTION],
|
|
3487
|
+
sections: [{
|
|
3488
|
+
title: "13F filings",
|
|
3489
|
+
lines: ["For each fund's reported position and its change over the quarter, run gloom fn 13F <symbol>."],
|
|
3490
|
+
}],
|
|
3491
|
+
examples: ["13f AAPL", "13f BP:NYSE"],
|
|
3492
|
+
},
|
|
3493
|
+
execute: (args, ctx) => runHolders(args, ctx, "13f", new Set(["institution", "fund"])),
|
|
3494
|
+
},
|
|
3495
|
+
{
|
|
3496
|
+
name: "filings",
|
|
3497
|
+
description: "Fetch recent SEC filings; a listing abroad gets its exchange's announcements page, and ASX's list",
|
|
3498
|
+
help: {
|
|
3499
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3500
|
+
usage: ["filings <symbol> [--count <n>] [--form <form>]"],
|
|
3501
|
+
options: [
|
|
3502
|
+
{ flags: "--count <n>", description: "Number of filings (default 15)" },
|
|
3503
|
+
{ flags: "--form <form>", description: "Only this form and its amendments, such as 10-K or 13D" },
|
|
3504
|
+
EXCHANGE_OPTION,
|
|
3505
|
+
],
|
|
3506
|
+
examples: ["filings AAPL", "filings BHP:ASX", "filings NST.AX", "filings AAPL --count 40 --json", "filings CAR --form 13G"],
|
|
3507
|
+
},
|
|
3508
|
+
execute: runFilings,
|
|
3509
|
+
},
|
|
3510
|
+
{
|
|
3511
|
+
name: "news",
|
|
3512
|
+
description: "Fetch market headlines, news for one symbol, or stories matching text",
|
|
3513
|
+
help: {
|
|
3514
|
+
group: CLI_COMMAND_GROUPS.companyData,
|
|
3515
|
+
usage: ["news [symbol] [--feed <feed>]", "news <query>"],
|
|
3516
|
+
options: [
|
|
3517
|
+
{ flags: "--feed <feed>", description: "latest, top, or breaking for market news (default latest)" },
|
|
3518
|
+
EXCHANGE_OPTION,
|
|
3519
|
+
],
|
|
3520
|
+
sections: [{
|
|
3521
|
+
title: "Text",
|
|
3522
|
+
lines: [
|
|
3523
|
+
"Several words, in quotes or not, search the stories that mention them. So does a single word of three letters or more that no listing answers to, and a futures board code such as TTF, a gas benchmark rather than a ticker. A real ticker always gives its own news. The best matches print newest first, 20 unless --limit says otherwise; a search takes neither --feed nor --exchange.",
|
|
3524
|
+
"A shell drops the quotes around one word, so news \"gold\" is the ticker GOLD. Keep them to search the text: news '\"gold\"'. When a word you type is also the name of the company it found, and the stories that mention it are mostly about something else, the listing says so under its stories with that command.",
|
|
3525
|
+
],
|
|
3526
|
+
}],
|
|
3527
|
+
examples: ["news", "news TSLA", "news SAN:EPA", "news TTF", "news \"european gas\"", "news '\"gold\"'", "news --feed top --limit 10"],
|
|
3528
|
+
},
|
|
3529
|
+
execute: runNews,
|
|
3530
|
+
},
|
|
3531
|
+
{
|
|
3532
|
+
name: "fx",
|
|
3533
|
+
description: "Convert a currency into your base currency, quote a pair such as USD/NGN, or convert an amount such as fx 1234.5 EUR/USD",
|
|
3534
|
+
help: {
|
|
3535
|
+
group: CLI_COMMAND_GROUPS.markets,
|
|
3536
|
+
usage: ["fx <currency>", "fx <base>/<quote>", "fx <amount> <from>/<to>", "fx <amount> <from> <to>", "fx <pair> --date <day>", FX_FORWARD_USAGE],
|
|
3537
|
+
options: [
|
|
3538
|
+
{ flags: "--date <day>", description: `The rate at that day's daily FX close, such as a transaction date (${DATE_OPTION_EXAMPLES}); a weekend or holiday reads the trading day before it` },
|
|
3539
|
+
{ flags: "--forward <tenor>", description: "The forward implied by both currencies' short rates (covered interest parity), not a dealer quote: 30d, 2w, 3m, up to 1y; today's only, so not with --date" },
|
|
3540
|
+
],
|
|
3541
|
+
sections: [{
|
|
3542
|
+
title: "Direction",
|
|
3543
|
+
lines: [
|
|
3544
|
+
"A bare code is one unit of it in your base currency. A pair reads as markets quote it: USD/NGN is how many NGN one USD buys. Two codes neither of which is USD, such as ZAR/NGN, cross through their USD rates.",
|
|
3545
|
+
"A currency's name stands for its code: fx yen is JPY, fx rand ZAR, fx Swiss franc CHF, and the answer says which it read. A name several currencies share (peso, dollar, franc, krona, rupee) lists them: write one as its code.",
|
|
3546
|
+
"The answer states both directions: 1 NGN = 0.000752791 USD (USD/NGN 1328.39). --json, --csv and --ndjson carry rate (base currency per currency), pair, inverse, asOf and stale.",
|
|
3547
|
+
],
|
|
3548
|
+
}, {
|
|
3549
|
+
title: "Amounts",
|
|
3550
|
+
lines: [
|
|
3551
|
+
"An amount in front converts it from the first currency to the second: fx 1234.5 EUR/USD, or fx 1234,5 EUR USD. It takes either decimal mark (1.234,5 and 1,234.5 are 1234.5) and reads as an amount of the first currency: 1,234 or 1.234 EUR are thousands, a third decimal being below a cent, while in a currency with three decimals (KWD) fx asks which. An amount and one currency converts to USD. The answer gives the converted amount, then the rate it used, dated by the source line; --json adds amount and converted.",
|
|
3552
|
+
],
|
|
3553
|
+
}, {
|
|
3554
|
+
title: "A past day",
|
|
3555
|
+
lines: [
|
|
3556
|
+
"--date gives the rate at that day's daily FX close instead of the latest: fx USD/CAD --date 2026-09-02, or with an amount, fx 1250 USD/CAD --date 2026-09-02. Each currency's USD pair is read at its close of that day and crossed through USD, as the latest rate is. A weekend or market holiday has no close: the answer then gives the trading day before it and says so. The source line dates the close; --json adds date (the day asked for) and asOf (the close used).",
|
|
3557
|
+
],
|
|
3558
|
+
}, {
|
|
3559
|
+
title: "Forwards",
|
|
3560
|
+
lines: [
|
|
3561
|
+
"--forward <tenor> gives the forward implied by covered interest parity: spot × (1 + quote rate × days/basis) / (1 + base rate × days/basis), with the forward points (pips of 0.0001, 0.01 against JPY) and the premium over spot for the period and a year. It is implied by rates, not a dealer quote.",
|
|
3562
|
+
"The rates are SOFR (USD), €STR (EUR), SONIA (GBP) and TIIE 28d (MXN), each with its day and publisher. A pair whose rate is missing says which one. Tenors count calendar days from today: 30d, 2w, 3m, 1y. The forward is today's: it does not take --date, as only the latest short rates are served.",
|
|
3563
|
+
],
|
|
3564
|
+
}],
|
|
3565
|
+
examples: ["fx EUR", "fx NGN", "fx yen", "fx USD/NGN", "fx ZAR/NGN", "fx 1234.5 EUR/USD", "fx 1234,5 EUR USD", "fx USD/CAD --date 2026-09-02", "fx JPY --json", "fx USD/MXN --forward 30d", "fx EUR/USD --forward 3m"],
|
|
3566
|
+
},
|
|
3567
|
+
execute: runFx,
|
|
3568
|
+
},
|
|
3569
|
+
];
|