gloomberb 0.17.4 → 0.17.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -1
- package/patches/electrobun@1.18.1.patch +80 -0
- package/src/api-client/chat.ts +9 -0
- package/src/api-client/commodity-balances.ts +77 -0
- package/src/api-client/data.ts +35 -3
- package/src/api-client/earnings.ts +10 -0
- package/src/api-client/index.ts +5 -0
- package/src/api-client/insider-signals.ts +75 -0
- package/src/api-client/market-discovery.ts +1 -1
- package/src/api-client/normalizers.ts +1 -0
- package/src/api-client/paths.ts +9 -0
- package/src/api-client/research-activity.ts +10 -3
- package/src/api-client/socket.ts +10 -0
- package/src/api-client/types.ts +25 -1
- package/src/app/pane-runtime/ticker-open-runtime.ts +2 -1
- package/src/app/runtime/broker-import.ts +23 -21
- package/src/app/runtime/plugin-bindings.ts +13 -7
- package/src/app/runtime/update.ts +64 -4
- package/src/app.tsx +2 -0
- package/src/architecture/core-builtin-allowlist.json +2 -10
- package/src/ask-gloom/pane-context.ts +11 -1
- package/src/brokers/broker-portfolio-sync.ts +66 -0
- package/src/brokers/sync-broker-instance.ts +75 -41
- package/src/cli/commands/market.ts +101 -34
- package/src/cli/commands/options-chain.ts +20 -6
- package/src/cli/commands/overview.ts +54 -13
- package/src/cli/commands/search.ts +1 -1
- package/src/cli/commands/system.ts +19 -1
- package/src/cli/commands/ticker.ts +24 -4
- package/src/cli/context.ts +13 -0
- package/src/cli/errors.ts +15 -2
- package/src/cli/fx-pair.ts +77 -0
- package/src/cli/glossary.ts +34 -4
- package/src/cli/history-rows.ts +25 -11
- package/src/cli/index.ts +30 -12
- package/src/cli/listing-arg.ts +61 -138
- package/src/cli/listing-suggestion.ts +3 -21
- package/src/cli/pane-functions/catalog.ts +50 -0
- package/src/cli/pane-functions/freshness.ts +44 -9
- package/src/cli/pane-functions/function-help.ts +9 -9
- package/src/cli/pane-functions/headless.ts +19 -7
- package/src/cli/pane-functions/index.ts +63 -5
- package/src/cli/pane-functions/market-session.ts +18 -3
- package/src/cli/pane-functions/resolver.ts +29 -8
- package/src/cli/pane-functions/screenshot.ts +2 -0
- package/src/cli/report-tables.ts +11 -4
- package/src/cli/result.ts +2 -0
- package/src/cli/series-search.ts +6 -1
- package/src/cli/ticker-resolution.ts +1 -2
- package/src/cli/unknown-command.ts +18 -0
- package/src/components/chart/composite/composite-chart.tsx +80 -19
- package/src/components/chart/composite/format.ts +17 -0
- package/src/components/chart/composite/legend.tsx +26 -15
- package/src/components/chart/composite/panel-labels.ts +38 -0
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +25 -7
- package/src/components/chart/composite/price-axis-labels.tsx +16 -3
- package/src/components/chart/composite/scene.ts +60 -2
- package/src/components/chart/composite/series-axis-tags.tsx +67 -0
- package/src/components/chart/composite/tools.ts +2 -2
- package/src/components/chart/composite/types.ts +28 -0
- package/src/components/chart/core/view-reset.ts +46 -0
- package/src/components/command-bar/commands/collection.ts +1 -1
- package/src/components/command-bar/commands/direct/index.ts +48 -1
- package/src/components/command-bar/commands/registry.ts +8 -0
- package/src/components/command-bar/list/model.ts +7 -0
- package/src/components/command-bar/pane-templates/workflow.ts +5 -0
- package/src/components/command-bar/panel/prompt-binding.ts +8 -0
- package/src/components/command-bar/routes/root/command-items.ts +17 -2
- package/src/components/command-bar/routes/root/pick-prefetch.ts +38 -0
- package/src/components/command-bar/routes/root/provider-search.ts +6 -0
- package/src/components/command-bar/routes/root/runtime.ts +3 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -1
- package/src/components/command-bar/routes/root/shortcut-items.ts +1 -1
- package/src/components/command-bar/routes/ticker-search/actions.ts +34 -0
- package/src/components/command-bar/surface/index.tsx +7 -1
- package/src/components/command-bar/workflow/collection-actions.ts +6 -5
- package/src/components/command-bar/workflow/ops.ts +8 -5
- package/src/components/command-bar/workflow/pane-reuse.ts +3 -2
- package/src/components/command-bar/workflow/tickers.ts +1 -1
- package/src/components/form-modal/confirm.tsx +1 -0
- package/src/components/form-modal/request.ts +2 -0
- package/src/components/layout/detached-pane-shell.tsx +6 -1
- package/src/components/layout/pane/content.tsx +11 -8
- package/src/components/layout/pane/sidebar.tsx +24 -11
- package/src/components/layout/pane/ticker-fate-gate.tsx +56 -0
- package/src/components/layout/shell/index.tsx +5 -1
- package/src/components/layout/shell/menu.ts +8 -0
- package/src/components/layout/shell/pane/capture.ts +29 -12
- package/src/components/onboarding/wizard-model.ts +1 -1
- package/src/components/onboarding/wizard-positions.ts +18 -10
- package/src/components/ticker/badge/format.ts +64 -15
- package/src/components/ticker/badge/index.tsx +32 -11
- package/src/components/ticker/badge/list.tsx +4 -3
- package/src/components/ticker/badge/text.tsx +2 -2
- package/src/components/ticker/list-table-view.tsx +8 -1
- package/src/components/ui/confirm-dialog.tsx +30 -4
- package/src/components/ui/data-table/opentui/index.tsx +61 -20
- package/src/components/ui/data-table/opentui/model.ts +104 -0
- package/src/components/ui/fields.tsx +5 -0
- package/src/components/ui/inline-quick-add.tsx +4 -1
- package/src/components/ui/query-bar-keys.tsx +29 -5
- package/src/components/ui/query-bar.tsx +29 -4
- package/src/components/ui/windowed-list.tsx +27 -0
- package/src/data/config/store/normalize.ts +8 -0
- package/src/docs/snapshot.json +248 -5
- package/src/i18n/es.ts +3 -1
- package/src/i18n/ja.ts +3 -1
- package/src/i18n/ko.ts +3 -1
- package/src/i18n/zh-cn.ts +3 -1
- package/src/i18n/zh-tw.ts +3 -1
- package/src/layout-marketplace/cloud.ts +16 -5
- package/src/layout-marketplace/gallery.tsx +4 -4
- package/src/layout-marketplace/linked-sync.ts +9 -3
- package/src/layout-marketplace/linked.ts +24 -3
- package/src/layout-marketplace/model.ts +39 -4
- package/src/market-data/coordinator/events.ts +5 -1
- package/src/market-data/coordinator/index.ts +38 -1
- package/src/market-data/coordinator/quotes.ts +4 -0
- package/src/market-data/futures-contract.ts +53 -0
- package/src/market-data/hooks.tsx +225 -30
- package/src/market-data/market/bar-period.ts +71 -0
- package/src/market-data/market/format.ts +14 -2
- package/src/market-data/market/freshness.ts +28 -1
- package/src/market-data/market/status.ts +152 -13
- package/src/market-data/model-dividend-yield.ts +76 -0
- package/src/market-data/quotes/freshness.ts +30 -1
- package/src/{plugins/builtin/portfolio-list → market-data/quotes}/live-valuation.ts +6 -6
- package/src/market-data/quotes/regular-session-valuation.ts +32 -1
- package/src/plugins/builtin/analytics/metrics.ts +8 -4
- package/src/plugins/builtin/analytics/risk-headless.ts +12 -5
- package/src/plugins/builtin/analytics/risk-model.ts +74 -15
- package/src/plugins/builtin/analytics/risk-pane.tsx +6 -6
- package/src/plugins/builtin/broker-manager/detail.tsx +2 -2
- package/src/plugins/builtin/broker-manager/model.ts +2 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +0 -1
- package/src/plugins/builtin/chart-composer/editor/controller.ts +14 -10
- package/src/plugins/builtin/chart-composer/formula.ts +233 -0
- package/src/plugins/builtin/chart-composer/headless.ts +27 -2
- package/src/plugins/builtin/chart-composer/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/pane.tsx +41 -10
- package/src/plugins/builtin/chart-composer/presets.ts +91 -2
- package/src/plugins/builtin/chart-composer/quick-add.tsx +9 -2
- package/src/plugins/builtin/chart-composer/series-catalog.ts +44 -11
- package/src/plugins/builtin/chart-composer/series-expression.ts +1 -1
- package/src/plugins/builtin/chart-composer/settings.ts +16 -4
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/content/channel-navigation.ts +17 -6
- package/src/plugins/builtin/chat/content/footer.tsx +5 -2
- package/src/plugins/builtin/chat/content/index.tsx +12 -5
- package/src/plugins/builtin/chat/content/mentions.ts +50 -21
- package/src/plugins/builtin/chat/content/scroll.ts +12 -5
- package/src/plugins/builtin/chat/content/snapshot.ts +11 -0
- package/src/plugins/builtin/chat/content/transcript.tsx +48 -37
- package/src/plugins/builtin/chat/content/whole-message-copy.ts +44 -2
- package/src/plugins/builtin/chat/controller/index.ts +88 -17
- package/src/plugins/builtin/chat/controller/mention-candidates.ts +104 -0
- package/src/plugins/builtin/chat/controller/messages.ts +14 -2
- package/src/plugins/builtin/chat/controller/realtime.ts +9 -0
- package/src/plugins/builtin/chat/controller/state.ts +25 -0
- package/src/plugins/builtin/chat/controller/unread-people.ts +240 -0
- package/src/plugins/builtin/chat/controller/view.ts +4 -0
- package/src/plugins/builtin/chat/conversation-recency.ts +73 -0
- package/src/plugins/builtin/chat/conversations.ts +12 -5
- package/src/plugins/builtin/chat/index.tsx +1 -1
- package/src/plugins/builtin/chat/layout.ts +23 -5
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -0
- package/src/plugins/builtin/chat/share/flow.tsx +18 -9
- package/src/plugins/builtin/chat/sidebar-rows.ts +18 -5
- package/src/plugins/builtin/chat/sidebar.tsx +6 -1
- package/src/plugins/builtin/chat/unread-badge.ts +38 -7
- package/src/plugins/builtin/cloud/askg/manifest.ts +4 -3
- package/src/plugins/builtin/cloud/askg/pane-attachment.ts +2 -3
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +16 -5
- package/src/plugins/builtin/commodities-energy/index.tsx +23 -4
- package/src/plugins/builtin/commodity-balances/client.ts +51 -193
- package/src/plugins/builtin/commodity-balances/headless.ts +10 -3
- package/src/plugins/builtin/commodity-balances/model.ts +5 -180
- package/src/plugins/builtin/commodity-balances/pane.tsx +3 -0
- package/src/plugins/builtin/custom-view/index.tsx +1 -1
- package/src/plugins/builtin/custom-view/loader.ts +57 -7
- package/src/plugins/builtin/custom-view/sources.ts +146 -0
- package/src/plugins/builtin/custom-view/view-spec.ts +37 -1
- package/src/plugins/builtin/earnings/board-model.ts +33 -3
- package/src/plugins/builtin/earnings/board-table.ts +59 -14
- package/src/plugins/builtin/earnings/board.tsx +36 -3
- package/src/plugins/builtin/earnings/headless.ts +24 -5
- package/src/plugins/builtin/earnings/history-model.ts +9 -1
- package/src/plugins/builtin/earnings/history-table.ts +16 -5
- package/src/plugins/builtin/earnings/history.tsx +40 -10
- package/src/plugins/builtin/earnings/index.tsx +3 -1
- package/src/plugins/builtin/earnings/no-earnings.ts +52 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +4 -1
- package/src/plugins/builtin/econ-statistics/headless.ts +5 -0
- package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
- package/src/plugins/builtin/econ-statistics/view.ts +4 -0
- package/src/plugins/builtin/futures/model.ts +5 -4
- package/src/plugins/builtin/futures/series.ts +38 -1
- package/src/plugins/builtin/futures-curve/client.ts +4 -4
- package/src/plugins/builtin/futures-curve/headless.ts +33 -8
- package/src/plugins/builtin/futures-curve/index.tsx +4 -4
- package/src/plugins/builtin/futures-curve/model.ts +109 -6
- package/src/plugins/builtin/futures-curve/pane.tsx +24 -13
- package/src/plugins/builtin/insider/headless.ts +74 -4
- package/src/plugins/builtin/insider/index.tsx +99 -5
- package/src/plugins/builtin/insider/signals-client.ts +69 -0
- package/src/plugins/builtin/insider/signals-model.ts +146 -0
- package/src/plugins/builtin/insider/signals.tsx +176 -0
- package/src/plugins/builtin/iv-history/client.ts +41 -2
- package/src/plugins/builtin/iv-history/headless.ts +24 -16
- package/src/plugins/builtin/iv-history/screen-pane.tsx +25 -7
- package/src/plugins/builtin/jodi/client.ts +42 -205
- package/src/plugins/builtin/jodi/headless.ts +13 -4
- package/src/plugins/builtin/jodi/model.ts +18 -43
- package/src/plugins/builtin/jodi/pane.tsx +5 -0
- package/src/plugins/builtin/kelly-sizer/headless.ts +1 -1
- package/src/plugins/builtin/kelly-sizer/history.ts +5 -3
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/macro-day/release-table.ts +3 -3
- package/src/plugins/builtin/market-movers/client.ts +32 -6
- package/src/plugins/builtin/market-movers/headless.ts +7 -2
- package/src/plugins/builtin/market-movers/model.ts +64 -3
- package/src/plugins/builtin/market-movers/screener.ts +20 -9
- package/src/plugins/builtin/market-valuation/detail.tsx +7 -2
- package/src/plugins/builtin/market-valuation/headless.ts +17 -4
- package/src/plugins/builtin/market-valuation/pane.tsx +7 -3
- package/src/plugins/builtin/market-valuation/view.ts +10 -0
- package/src/plugins/builtin/mna/model.ts +2 -1
- package/src/plugins/builtin/options-positioning/client.ts +10 -0
- package/src/plugins/builtin/options-positioning/headless.ts +88 -13
- package/src/plugins/builtin/options-positioning/index.tsx +2 -2
- package/src/plugins/builtin/options-positioning/model.ts +47 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +55 -26
- package/src/plugins/builtin/options-scenario/charts.tsx +13 -4
- package/src/plugins/builtin/options-scenario/client.ts +111 -17
- package/src/plugins/builtin/options-scenario/collar.ts +141 -0
- package/src/plugins/builtin/options-scenario/csp.ts +1 -1
- package/src/plugins/builtin/options-scenario/freshness.ts +27 -0
- package/src/plugins/builtin/options-scenario/headless.ts +28 -12
- package/src/plugins/builtin/options-scenario/index.tsx +1 -1
- package/src/plugins/builtin/options-scenario/model.ts +55 -5
- package/src/plugins/builtin/options-scenario/pane.tsx +24 -5
- package/src/plugins/builtin/perps/client.ts +77 -6
- package/src/plugins/builtin/perps/headless.ts +25 -14
- package/src/plugins/builtin/perps/index.tsx +1 -1
- package/src/plugins/builtin/perps/model.ts +32 -0
- package/src/plugins/builtin/perps/pane.tsx +5 -4
- package/src/plugins/builtin/portfolio-list/allocation.ts +9 -4
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +39 -20
- package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +15 -7
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +1 -1
- package/src/plugins/builtin/portfolio-list/column-values.ts +4 -3
- package/src/plugins/builtin/portfolio-list/command-bar.ts +1 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +10 -3
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +36 -4
- package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +1 -1
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +35 -6
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +2 -1
- package/src/plugins/builtin/portfolio-list/settings.ts +22 -0
- package/src/plugins/builtin/portfolio-list/since-last-seen.ts +197 -0
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +14 -2
- package/src/plugins/builtin/rate-path/model.ts +1 -1
- package/src/plugins/builtin/rate-path/pane.tsx +2 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +14 -5
- package/src/plugins/builtin/research/event-model.ts +41 -49
- package/src/plugins/builtin/research/event-sources.ts +1 -1
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +6 -0
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
- package/src/plugins/builtin/reverse-dcf/headless.ts +25 -9
- package/src/plugins/builtin/reverse-dcf/index.tsx +4 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +39 -5
- package/src/plugins/builtin/reverse-dcf/pane.tsx +16 -6
- package/src/plugins/builtin/sectors/client.ts +46 -19
- package/src/plugins/builtin/sectors/sector-model.ts +22 -10
- package/src/plugins/builtin/shared/earnings-report.ts +48 -1
- package/src/plugins/builtin/shared/history-span.ts +55 -0
- package/src/plugins/builtin/shared/report-freshness.ts +20 -11
- package/src/plugins/builtin/shared/series-detail-chart.tsx +5 -2
- package/src/plugins/builtin/shared/series-list-detail.tsx +7 -3
- package/src/plugins/builtin/shared/ticker-surface.ts +16 -2
- package/src/plugins/builtin/shared/use-quote-board.ts +12 -5
- package/src/plugins/builtin/themes/headless.ts +15 -2
- package/src/plugins/builtin/thirteenf/api.ts +60 -43
- package/src/plugins/builtin/thirteenf/data.ts +76 -35
- package/src/plugins/builtin/thirteenf/headless.ts +2 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +10 -2
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +85 -17
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +92 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +26 -8
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +4 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +95 -22
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +17 -3
- package/src/plugins/builtin/yield-curve/curves.ts +15 -1
- package/src/plugins/builtin/yield-curve/headless.ts +17 -7
- package/src/plugins/builtin/yield-curve/history.ts +2 -0
- package/src/plugins/builtin/yield-curve/index.tsx +23 -18
- package/src/plugins/builtin/yield-curve/series.ts +19 -9
- package/src/plugins/builtin/yield-curve/treasury-data.ts +2 -0
- package/src/plugins/builtin/yield-curve/world.ts +4 -0
- package/src/plugins/registry/host-actions.ts +3 -0
- package/src/{plugins/builtin/portfolio-list → portfolio}/mutations.ts +6 -6
- package/src/portfolio/valuation-basis.ts +4 -21
- package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
- package/src/remote/controller.ts +27 -7
- package/src/remote/schema.ts +25 -9
- package/src/renderers/browser/app-services.ts +7 -1
- package/src/renderers/browser/config-host.ts +55 -2
- package/src/renderers/browser/first-screen.ts +193 -0
- package/src/renderers/browser/main.tsx +59 -7
- package/src/renderers/cloudflare/http-proxy.ts +21 -13
- package/src/renderers/dom/desktop/controls.tsx +2 -0
- package/src/renderers/dom/desktop/query-bar.tsx +1 -1
- package/src/renderers/dom/dom-ui-host.tsx +2 -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/index.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -7
- package/src/renderers/electrobun/bun/desktop/http-stream.ts +21 -6
- package/src/renderers/electrobun/bun/desktop/update.ts +132 -14
- package/src/renderers/electrobun/shared/protocol.ts +7 -4
- package/src/renderers/electrobun/view/http-fetch.ts +12 -1
- package/src/renderers/electrobun/view/update-host.ts +2 -2
- package/src/shares/bug-report-dialog.tsx +162 -0
- package/src/shares/bug-report-flow.tsx +84 -0
- package/src/shares/bug-report.ts +42 -0
- package/src/shares/capture-flow.ts +19 -0
- package/src/shares/capture-preview.tsx +129 -0
- package/src/shares/chat.ts +10 -1
- package/src/shares/dialog.tsx +16 -91
- package/src/shares/flow.tsx +12 -16
- package/src/shares/personal-pane.ts +35 -1
- package/src/sources/listing-symbols.ts +8 -4
- package/src/state/app/context/index.tsx +13 -0
- package/src/state/app/tab-cursor.ts +85 -0
- package/src/sync/core-contributors.ts +206 -59
- package/src/telemetry/usage-counts.ts +16 -0
- package/src/test-support/config-store.ts +31 -0
- package/src/test-support/moved-symbols.ts +64 -0
- package/src/theme/colors.ts +30 -0
- package/src/tickers/listing-fate.ts +242 -0
- package/src/tickers/open-target.ts +61 -11
- package/src/tickers/search/index.ts +45 -3
- package/src/tickers/search/ranking.ts +19 -2
- package/src/tickers/search/types.ts +3 -0
- package/src/tickers/search/upsert.ts +22 -7
- package/src/tickers/ticker-changes.ts +26 -9
- package/src/time-series/bar-spacing.ts +19 -0
- package/src/time-series/chart-data.ts +37 -13
- package/src/time-series/field-catalog.ts +5 -2
- package/src/time-series/forward-valuation.ts +10 -7
- package/src/time-series/fundamentals.ts +49 -11
- package/src/time-series/hooks.ts +2 -0
- package/src/time-series/live-bars.ts +3 -1
- package/src/time-series/parsed-history-cache.ts +2 -0
- package/src/time-series/price-units.ts +69 -0
- package/src/time-series/resolve.ts +71 -11
- package/src/time-series/session-bar.ts +138 -0
- package/src/time-series/spec.ts +20 -3
- package/src/time-series/studies.ts +211 -12
- package/src/time-series/transforms.ts +2 -0
- package/src/time-series/types.ts +11 -1
- package/src/time-series/valuation-price.ts +13 -0
- package/src/types/config.ts +24 -1
- package/src/types/financials.ts +2 -0
- package/src/types/headless.ts +27 -0
- package/src/types/plugin.ts +7 -0
- package/src/ui/host.tsx +5 -0
- package/src/updater/channel.ts +51 -0
- package/src/updater/github-releases.ts +85 -16
- package/src/updater/index.ts +128 -17
- package/src/utils/asset-category.ts +20 -0
- package/src/utils/datetime-format.ts +7 -1
- package/src/utils/decimal-comma.ts +83 -3
- package/src/utils/fundamentals.ts +7 -2
- package/src/utils/http-proxy-response.ts +66 -9
- package/src/utils/plugin-proxy-hosts.json +1 -5
- package/src/utils/semver.ts +47 -2
- package/src/utils/typo-distance.ts +22 -0
- package/src/plugins/builtin/shared/zip-text.ts +0 -46
|
@@ -27,7 +27,8 @@ import {
|
|
|
27
27
|
getExtendedSessionDisplay,
|
|
28
28
|
getQuoteSessionFields,
|
|
29
29
|
getRegularSessionDisplay,
|
|
30
|
-
|
|
30
|
+
quoteHeadlineTime,
|
|
31
|
+
quoteMarketStateLabel,
|
|
31
32
|
} from "../../market-data/market/status";
|
|
32
33
|
import type { FuturesContractTermsPayload } from "../../api-client/futures-curve";
|
|
33
34
|
import { futuresContractLines, futuresSettleText, isFuturesInstrument, loadFuturesContractTerms } from "../../market-data/futures-contract";
|
|
@@ -59,8 +60,10 @@ import { NotesFiles } from "../../plugins/builtin/notes/files";
|
|
|
59
60
|
import { isKnownNonUsListing } from "../../utils/sec";
|
|
60
61
|
import { canonicalExchange, exchangeLabel, isKnownExchangeCode, isUsListingExchange } from "../../utils/exchanges";
|
|
61
62
|
import {
|
|
62
|
-
failIfNotTraded, ListingArgError, listingChoiceNotes, listingIdentity, loadBareVenues,
|
|
63
|
+
failIfNotTraded, failMovedSymbol, ListingArgError, listingChoiceNotes, listingFateLoader, listingIdentity, loadBareVenues,
|
|
64
|
+
resolveCliListing, type CliListing, type ListingDataRequest,
|
|
63
65
|
} from "../listing-arg";
|
|
66
|
+
import { movedAwayChange } from "../../tickers/ticker-changes";
|
|
64
67
|
import { isNotATickerMessage } from "../not-a-ticker";
|
|
65
68
|
import { sharesOutstandingInReceipts } from "../../utils/depositary-receipt";
|
|
66
69
|
import { cliFreshnessFooter } from "../result";
|
|
@@ -714,7 +717,7 @@ export async function buildTickerReport({
|
|
|
714
717
|
listingVenueLabel(listingExchange, quote, financials, tickerFile) || undefined,
|
|
715
718
|
(quote?.currency || financials.quoteMetadata?.currency || tickerFile?.metadata.currency)
|
|
716
719
|
? `Currency ${quote?.currency || financials.quoteMetadata?.currency || tickerFile?.metadata.currency}` : undefined,
|
|
717
|
-
|
|
720
|
+
quoteMarketStateLabel(quote) ?? undefined,
|
|
718
721
|
quote?.dataSource ? `Source ${quote.dataSource.toUpperCase()}` : undefined,
|
|
719
722
|
].filter((part): part is string => !!part);
|
|
720
723
|
if (summaryParts.length > 0) {
|
|
@@ -795,7 +798,7 @@ export async function buildTickerReport({
|
|
|
795
798
|
// A perpetual's price comes with no volume; its 0 means not reported, not no trades.
|
|
796
799
|
["Volume", quote.volume != null && !(quote.volume === 0 && isCryptoVenuePerpetual(quote.instrumentType, quote.name))
|
|
797
800
|
? formatNumber(quote.volume, 0) : "—"],
|
|
798
|
-
["Updated", formatTimestamp(quote
|
|
801
|
+
["Updated", formatTimestamp(quoteHeadlineTime(quote))],
|
|
799
802
|
]);
|
|
800
803
|
|
|
801
804
|
if (futuresContract) appendMetricSection(lines, "Contract", futuresContractMetrics(futuresContract, quote.symbol ?? symbol));
|
|
@@ -989,6 +992,17 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
|
|
|
989
992
|
const toBase = createBaseConverter(dataProvider, config.baseCurrency);
|
|
990
993
|
// A bare symbol's venues load beside its research, to name the company it was read as when several list it.
|
|
991
994
|
const venues = loadBareVenues(listing, { store, dataProvider });
|
|
995
|
+
// So does what became of the symbol, read from the research's quote as `history` reads it: a
|
|
996
|
+
// company that moved (HFC became DINO), with nothing new listed there, has no profile of its own.
|
|
997
|
+
const researchQuote = Promise.withResolvers<Quote | null>();
|
|
998
|
+
const fate = listingFateLoader(listing, { dataProvider }, researchQuote.promise);
|
|
999
|
+
const request: ListingDataRequest = { command: "ticker", noun: "research", fate };
|
|
1000
|
+
/** Nothing to show: a company that moved is under its new symbol. */
|
|
1001
|
+
const failIfMovedAway = async () => {
|
|
1002
|
+
researchQuote.resolve(null);
|
|
1003
|
+
const moved = movedAwayChange((await fate()).change);
|
|
1004
|
+
if (moved) failMovedSymbol(moved, request);
|
|
1005
|
+
};
|
|
992
1006
|
|
|
993
1007
|
let financials: TickerFinancials | null = null;
|
|
994
1008
|
try {
|
|
@@ -996,6 +1010,7 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
|
|
|
996
1010
|
} catch (error) {
|
|
997
1011
|
// A known exchange the symbol is not listed on: say where it is.
|
|
998
1012
|
await failIfNotTraded(listing, { store, dataProvider }, { fail: failCommand });
|
|
1013
|
+
await failIfMovedAway();
|
|
999
1014
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1000
1015
|
// A symbol no listing carries is not a failed fetch, and the message already names it.
|
|
1001
1016
|
if (isNotATickerMessage(reason)) failCommand(reason);
|
|
@@ -1016,10 +1031,15 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
|
|
|
1016
1031
|
);
|
|
1017
1032
|
if (!financials || (!hasResearchData && !tickerFile?.metadata.positions.some((position) => position.shares !== 0))) {
|
|
1018
1033
|
await failIfNotTraded(listing, { store, dataProvider }, { fail: failCommand });
|
|
1034
|
+
await failIfMovedAway();
|
|
1019
1035
|
failCommand(`No research data available for ${normalized}.`);
|
|
1020
1036
|
}
|
|
1021
1037
|
const resolvedFinancials = financials as TickerFinancials;
|
|
1022
1038
|
const quote = resolvedFinancials.quote;
|
|
1039
|
+
// The quote is the old line's (stopped, or named for the company that left): the profile and figures are another security's.
|
|
1040
|
+
researchQuote.resolve(quote ?? null);
|
|
1041
|
+
const { moved } = await fate();
|
|
1042
|
+
if (moved) failMovedSymbol(moved, request);
|
|
1023
1043
|
|
|
1024
1044
|
const notesFiles = new NotesFiles(dataDir);
|
|
1025
1045
|
const listingName = listingIdentity(listing, quote).name;
|
package/src/cli/context.ts
CHANGED
|
@@ -13,7 +13,9 @@ import { getLoadablePlugins } from "../plugins/catalog";
|
|
|
13
13
|
import { standaloneSeriesInvoker } from "../series/standalone";
|
|
14
14
|
import type { PluginRegistry } from "../plugins/registry";
|
|
15
15
|
import type { LoadedExternalPlugin } from "../plugins/loader";
|
|
16
|
+
import { apiClient } from "../api-client";
|
|
16
17
|
import { fail } from "./errors";
|
|
18
|
+
import { resolvePersistedCloudSessionToken } from "./pane-functions/cloud-session";
|
|
17
19
|
import type { ConfigContext, MarketContext } from "./types";
|
|
18
20
|
|
|
19
21
|
export { withCliServices, withConfigData, withMarketData } from "./scoped-context";
|
|
@@ -105,8 +107,18 @@ export async function initConfigData(): Promise<ConfigContext> {
|
|
|
105
107
|
return { config, persistence, store, dataDir };
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Market commands (`quote`, `compare`, `history`...) run with the session the
|
|
112
|
+
* app saved after sign-in, as `fn` does: Gloom Cloud answers a Pro account
|
|
113
|
+
* with real-time quotes and anyone else with delayed ones.
|
|
114
|
+
*/
|
|
115
|
+
function applyPersistedCloudSession(context: Pick<MarketContext, "persistence">): void {
|
|
116
|
+
apiClient.setSessionToken(resolvePersistedCloudSessionToken(context));
|
|
117
|
+
}
|
|
118
|
+
|
|
108
119
|
export async function initMarketData(options: CliContextOptions = {}): Promise<MarketContext> {
|
|
109
120
|
const context = await initConfigData();
|
|
121
|
+
applyPersistedCloudSession(context);
|
|
110
122
|
const plugins = options.plugins ?? getLoadablePlugins();
|
|
111
123
|
const capabilities = resolveCliCapabilities(context.config, plugins);
|
|
112
124
|
const dataProvider = new AssetDataRouter(null, [], context.persistence.resources);
|
|
@@ -131,6 +143,7 @@ export async function initCliServices(options: CliServicesOptions = {}) {
|
|
|
131
143
|
services.providerRouter.setConfigAccessor(() => config);
|
|
132
144
|
services.providerRouter.setBackgroundRevalidation(false);
|
|
133
145
|
await ensureCliServicesReady(services);
|
|
146
|
+
applyPersistedCloudSession(services);
|
|
134
147
|
return {
|
|
135
148
|
config,
|
|
136
149
|
dataDir,
|
package/src/cli/errors.ts
CHANGED
|
@@ -22,13 +22,16 @@ class CliFailure extends Error {
|
|
|
22
22
|
readonly code: string;
|
|
23
23
|
readonly details?: unknown;
|
|
24
24
|
readonly retryable?: boolean;
|
|
25
|
+
/** What a structured format carries beside the message; text prints the message alone. */
|
|
26
|
+
readonly fields?: Record<string, unknown>;
|
|
25
27
|
|
|
26
|
-
constructor(message: string, details?: unknown, code = CLI_ERROR_CODE, retryable?: boolean) {
|
|
28
|
+
constructor(message: string, details?: unknown, code = CLI_ERROR_CODE, retryable?: boolean, fields?: Record<string, unknown>) {
|
|
27
29
|
super(message);
|
|
28
30
|
this.name = "CliFailure";
|
|
29
31
|
this.code = code;
|
|
30
32
|
this.details = details;
|
|
31
33
|
this.retryable = retryable;
|
|
34
|
+
this.fields = fields;
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -36,12 +39,21 @@ export function fail(message: string, details?: unknown, code?: string): never {
|
|
|
36
39
|
throw new CliFailure(message, details, code);
|
|
37
40
|
}
|
|
38
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Fails with its own code and the fields `--json` reads beside the message:
|
|
44
|
+
* `{ ok: false, error: { code, message, ...fields } }`. Text prints the
|
|
45
|
+
* message alone, and the exit status is any failure's.
|
|
46
|
+
*/
|
|
47
|
+
export function failWithFields(message: string, code: string, fields: Record<string, unknown>): never {
|
|
48
|
+
throw new CliFailure(message, undefined, code, undefined, fields);
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
export function closeAndFail(persistence: AppPersistence, message: string, details?: string): never {
|
|
40
52
|
persistence.close();
|
|
41
53
|
fail(message, details);
|
|
42
54
|
}
|
|
43
55
|
|
|
44
|
-
function isCliFailure(error: unknown): error is CliFailure {
|
|
56
|
+
export function isCliFailure(error: unknown): error is CliFailure {
|
|
45
57
|
return error instanceof CliFailure;
|
|
46
58
|
}
|
|
47
59
|
|
|
@@ -57,6 +69,7 @@ function cliErrorObject(error: unknown): CliErrorObject {
|
|
|
57
69
|
message: error.message,
|
|
58
70
|
details: error.details,
|
|
59
71
|
retryable: error.retryable,
|
|
72
|
+
...error.fields,
|
|
60
73
|
};
|
|
61
74
|
}
|
|
62
75
|
// Gloom Cloud refused what was asked (an unknown series, a bad date): the input was wrong, not the program.
|
package/src/cli/fx-pair.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { currencyMinorDigits } from "../market-data/market/format";
|
|
2
|
+
import { parseTypedNumber, readCurrencyAmount } from "../utils/decimal-comma";
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* What `gloomberb fx` is asked for. A bare code is one unit of it in the base
|
|
3
6
|
* currency (`NGN`: USD per 1 NGN). A pair is market convention, BASE/QUOTE:
|
|
@@ -42,6 +45,80 @@ export function parseFxRequest(raw: string, baseCurrency: string): FxRequest | {
|
|
|
42
45
|
: { currency: parts[0]!, baseCurrency: baseCurrency.trim().toUpperCase() };
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
/** What `gloomberb fx` is asked for, with the amount to convert when one comes first. */
|
|
49
|
+
export interface FxArgs {
|
|
50
|
+
request: FxRequest;
|
|
51
|
+
/** The amount of `currency` to convert into `baseCurrency`; absent for a rate alone. */
|
|
52
|
+
amount?: number;
|
|
53
|
+
/** One currency after an amount names no target, so it converts to USD; the answer says so. */
|
|
54
|
+
toUsdByDefault?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Every form `fx` takes, for its usage line. */
|
|
58
|
+
export const FX_USAGE = "fx <currency> | fx <base>/<quote> | fx <amount> <from>/<to> | fx <amount> <from> <to>";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* `fx` arguments: a currency or a pair (`EUR`, `USD/NGN`), or an amount and
|
|
62
|
+
* what to convert it from and to (`1234.5 EUR/USD`, `1234,5 EUR USD`). The
|
|
63
|
+
* amount is an amount of the first currency and reads as cash does
|
|
64
|
+
* (`readCurrencyAmount`): `1.234,5` and `1,234.5` are 1234.5, and `1,234` or
|
|
65
|
+
* `1.234` are thousands unless that currency has three decimals, where they are
|
|
66
|
+
* refused naming both readings. An amount and one currency converts to USD
|
|
67
|
+
* (`toUsdByDefault`). The error sentence otherwise.
|
|
68
|
+
*/
|
|
69
|
+
export function parseFxArgs(args: readonly string[], baseCurrency: string): FxArgs | { error: string } {
|
|
70
|
+
if (args.length === 1) {
|
|
71
|
+
const request = parseFxRequest(args[0]!, baseCurrency);
|
|
72
|
+
if (!("error" in request)) return { request };
|
|
73
|
+
// A lone number is an amount with nothing to convert it from.
|
|
74
|
+
return parseTypedNumber(args[0]).kind === "invalid"
|
|
75
|
+
? request
|
|
76
|
+
: { error: `fx ${args[0]!.trim()} names no currency; convert an amount as fx ${args[0]!.trim()} EUR/USD.` };
|
|
77
|
+
}
|
|
78
|
+
if (args.length === 0 || args.length > 3) {
|
|
79
|
+
return { error: `fx takes a currency, a pair, or an amount and a pair (fx 1234.5 EUR/USD); got ${args.length} arguments.` };
|
|
80
|
+
}
|
|
81
|
+
const typed = args.map((arg) => arg.trim());
|
|
82
|
+
const [first, ...codes] = typed;
|
|
83
|
+
if (parseTypedNumber(first).kind === "invalid") {
|
|
84
|
+
// The amount typed after the pair (fx EUR/USD 100) belongs in front.
|
|
85
|
+
if (parseTypedNumber(typed.at(-1)).kind === "number") {
|
|
86
|
+
return { error: `Put the amount first: fx ${[typed.at(-1), ...typed.slice(0, -1)].join(" ")}.` };
|
|
87
|
+
}
|
|
88
|
+
// Two codes are one pair (fx NGN ZAR is NGN/ZAR), never two answers.
|
|
89
|
+
const pair = typed.length === 2 && typed.every((arg) => /^[A-Za-z]{3}$/.test(arg)) ? typed.join("/").toUpperCase() : "EUR/USD";
|
|
90
|
+
return { error: `fx takes one currency or pair; got ${typed.join(" ")}. Write a pair as ${pair}, or convert an amount as fx 1234.5 ${pair}.` };
|
|
91
|
+
}
|
|
92
|
+
const toUsdByDefault = codes.length === 1 && !codes[0]!.includes("/");
|
|
93
|
+
const request = parseFxRequest(codes.join("/"), "USD");
|
|
94
|
+
if ("error" in request) return request;
|
|
95
|
+
// The amount is in the currency converted from, so its units say what 1,234 means.
|
|
96
|
+
let amount: number;
|
|
97
|
+
try {
|
|
98
|
+
amount = readCurrencyAmount(first, "The amount", request.currency);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
101
|
+
}
|
|
102
|
+
return { request, amount, ...(toUsdByDefault ? { toUsdByDefault } : {}) };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* An amount in its currency's minor units, grouped (1,234.50 EUR; 1,235 JPY).
|
|
107
|
+
* One too small for them keeps six significant figures instead of reading 0.
|
|
108
|
+
*/
|
|
109
|
+
function formatFxAmount(amount: number, currency: string): string {
|
|
110
|
+
const digits = currencyMinorDigits(currency);
|
|
111
|
+
const rounded = Number(amount.toFixed(digits));
|
|
112
|
+
if (rounded === 0 && amount !== 0) return `${formatFxRate(amount)} ${currency}`;
|
|
113
|
+
const text = amount.toLocaleString("en-US", { minimumFractionDigits: digits, maximumFractionDigits: digits });
|
|
114
|
+
return `${text} ${currency}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** `1,234.50 EUR = 1,383.47 USD`: the amount and what it converts to at the rate. */
|
|
118
|
+
export function describeFxConversion(request: FxRequest, amount: number, rate: number): string {
|
|
119
|
+
return `${formatFxAmount(amount, request.currency)} = ${formatFxAmount(amount * rate, request.baseCurrency)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
45
122
|
/**
|
|
46
123
|
* The rate of `currency` in `baseCurrency` from each one's USD value (USD per
|
|
47
124
|
* unit), the way every cross is built. NaN when a leg is missing or unusable.
|
package/src/cli/glossary.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { cliStyles, cliTerminalWidth, renderDefinitions, renderSection, wrapText
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Plain-words meanings of the rates, auction, energy, perpetual-futures, portfolio,
|
|
5
|
-
* fund, short interest, quote-board, futures and economic-calendar terms the functions
|
|
5
|
+
* fund, short interest, quote-board, futures (basis, contango, roll yield) and economic-calendar terms the functions
|
|
6
6
|
* show in their rows, headers and footers.
|
|
7
7
|
* `catalog glossary` lists them, `catalog explain <term>` prints one, and
|
|
8
8
|
* `fn <function> --explain` and `portfolio show --explain` follow a report with
|
|
@@ -301,10 +301,11 @@ export const GLOSSARY: readonly GlossaryEntry[] = [
|
|
|
301
301
|
+ "The funding rate keeps its price close to the asset it tracks.",
|
|
302
302
|
},
|
|
303
303
|
{
|
|
304
|
-
term: "Funding rate", aliases: ["funding", "funding payment", "funding fee", "funding 8h"], functions: ["PERP"],
|
|
305
|
-
shown: ["Funding %/int", "Funding 8h", "Paid %/int"],
|
|
304
|
+
term: "Funding rate", aliases: ["funding", "funding payment", "funding fee", "funding 8h", "funding apr", "apr"], functions: ["PERP"],
|
|
305
|
+
shown: ["Funding %/int", "Funding 8h", "Paid %/int", "APR %"],
|
|
306
306
|
definition: "A regular payment between the two sides of a perpetual contract, set to pull its price toward the asset's. "
|
|
307
|
-
+ "Positive means longs pay shorts, negative means shorts pay longs."
|
|
307
|
+
+ "Positive means longs pay shorts, negative means shorts pay longs, so a basis trade (long spot, short the perpetual) collects it when positive. "
|
|
308
|
+
+ "APR % is the current 8h rate × 3 × 365: a simple annualisation of the rate now, not funding actually paid.",
|
|
308
309
|
},
|
|
309
310
|
{
|
|
310
311
|
term: "Open interest", short: "OI", aliases: ["open interest usd"], functions: ["PERP"],
|
|
@@ -473,6 +474,35 @@ export const GLOSSARY: readonly GlossaryEntry[] = [
|
|
|
473
474
|
definition: "A futures contract's official price for the day, set by its exchange from trading near the close. "
|
|
474
475
|
+ "The day's change and margin are measured from it, and it can differ from the last trade. FUT's PREV is the previous one.",
|
|
475
476
|
},
|
|
477
|
+
// Futures basis and curve shape (CTM, FUT, PERP).
|
|
478
|
+
{
|
|
479
|
+
term: "Futures basis", aliases: ["basis", "basis trade", "cash and carry", "cash-and-carry", "vs spot", "spot spread"],
|
|
480
|
+
functions: ["CTM", "FUT", "PERP"], shown: ["Vs Spot", "VS SPOT"],
|
|
481
|
+
definition: "The gap between a futures price and the spot price of what it tracks, in percent: positive when the future costs more, negative when it costs less. "
|
|
482
|
+
+ "It closes by expiry. A basis trade buys spot and sells the future to earn it. A perpetual has no expiry, so its funding rate plays that part.",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
term: "Annualised basis", aliases: ["annualized basis", "ann basis", "ann. basis", "basis annualised"], functions: ["CTM", "PERP"], shown: ["Ann Basis", "ANN BASIS"],
|
|
486
|
+
definition: "The basis restated as a yearly rate, so contracts of different length compare with each other and with a perpetual's funding APR: "
|
|
487
|
+
+ "(Last / spot - 1) × 365 / days to expiry, simple rather than compounded, on the latest trade against the spot quote. "
|
|
488
|
+
+ "Inside 30 days a few basis points of spot noise move it by whole points: 10 bp is about ±2 points at 18 days to expiry.",
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
term: "Contango", aliases: ["contango curve", "upward curve"], functions: ["CTM", "FUT"], shown: ["contango", "Contango"],
|
|
492
|
+
definition: "A futures curve that slopes up: each contract costs more than the one before it, and the future trades above spot. "
|
|
493
|
+
+ "A long rolling into the next contract pays that difference, a short earns it, and the basis closes as expiry nears.",
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
term: "Backwardation", aliases: ["backwardation curve", "downward curve", "inverted futures curve"], functions: ["CTM", "FUT"], shown: ["backwardation", "Backwardation"],
|
|
497
|
+
definition: "A futures curve that slopes down: later contracts cost less than nearer ones, and the future trades below spot, often when the asset is scarce now. "
|
|
498
|
+
+ "A long rolling into the next contract earns that difference, a short pays it.",
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
term: "Roll yield", aliases: ["roll", "roll return", "annualised roll yield", "annualized roll yield", "ann roll yield", "ann. roll yield"], functions: ["CTM", "FUT"],
|
|
502
|
+
shown: ["Ann. roll yield", "Annualized Roll Yield"],
|
|
503
|
+
definition: "What a long future earns or loses as it is rolled into the next contract. CTM's annualised roll yield is the front price over the next contract's "
|
|
504
|
+
+ "less 1, scaled to a year: (front / next - 1) × 365 / days between the two. It is positive in backwardation and negative in contango.",
|
|
505
|
+
},
|
|
476
506
|
// The economic calendar (ECO).
|
|
477
507
|
{
|
|
478
508
|
term: "Actual", aliases: ["actual release", "actual value"], functions: ["ECO"], shown: ["ACTUAL", "Actual"],
|
package/src/cli/history-rows.ts
CHANGED
|
@@ -16,6 +16,8 @@ import { getPricePointTimestamp } from "../utils/price-history";
|
|
|
16
16
|
import { pricePointIntegrityFlags, pricePointValues } from "../utils/price-history-integrity";
|
|
17
17
|
import { formatUtcTime } from "../utils/utc-time";
|
|
18
18
|
import { historyCoverageNotice } from "../sources/history-coverage";
|
|
19
|
+
import { barPeriodToDate, type BarPeriodToDate } from "../market-data/market/bar-period";
|
|
20
|
+
import { barSizeFromGap, medianBarGap } from "../time-series/bar-spacing";
|
|
19
21
|
|
|
20
22
|
const PRICE_KEYS = ["open", "high", "low", "close"] as const;
|
|
21
23
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -33,7 +35,7 @@ const BREAK_JUMP_RATIO = 100;
|
|
|
33
35
|
const BREAK_FLAGS: ReadonlySet<string> = new Set(["gap", "jump"]);
|
|
34
36
|
const FIRST_MONDAY_MS = Date.UTC(1970, 0, 5);
|
|
35
37
|
|
|
36
|
-
const BAR_INTERVAL_LABELS: Record<ManualChartResolution, string> = {
|
|
38
|
+
export const BAR_INTERVAL_LABELS: Record<ManualChartResolution, string> = {
|
|
37
39
|
"1m": "1-minute",
|
|
38
40
|
"5m": "5-minute",
|
|
39
41
|
"15m": "15-minute",
|
|
@@ -107,32 +109,25 @@ function isUtcMidnight(time: number): boolean {
|
|
|
107
109
|
return new Date(time).toISOString().endsWith("T00:00:00.000Z");
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
function medianGap(times: readonly number[]): number | null {
|
|
111
|
-
const finite = times.filter(Number.isFinite).sort((left, right) => left - right);
|
|
112
|
-
const gaps = finite.slice(1).map((time, index) => time - finite[index]!).filter((gap) => gap > 0).sort((left, right) => left - right);
|
|
113
|
-
return gaps.length > 0 ? gaps[Math.floor((gaps.length - 1) / 2)]! : null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
112
|
/** Without a declared cadence, bar spacing decides: daily bars may be stamped at the session open. */
|
|
117
113
|
function looksIntraday(times: readonly number[]): boolean {
|
|
118
114
|
const finite = times.filter(Number.isFinite);
|
|
119
115
|
if (finite.length < 2) return finite.some((time) => !isUtcMidnight(time));
|
|
120
|
-
const gap =
|
|
116
|
+
const gap = medianBarGap(finite);
|
|
121
117
|
return gap != null && gap < INTRADAY_MAX_GAP_MS;
|
|
122
118
|
}
|
|
123
119
|
|
|
124
120
|
/** The bar size served: as the source declared it, otherwise read from the typical spacing of the bars. */
|
|
125
121
|
function historyInterval(times: readonly number[], resolution: ManualChartResolution | null): ManualChartResolution | null {
|
|
126
122
|
if (resolution) return resolution;
|
|
127
|
-
const gap =
|
|
123
|
+
const gap = medianBarGap(times);
|
|
128
124
|
if (gap == null) return null;
|
|
129
125
|
if (gap < INTRADAY_MAX_GAP_MS) {
|
|
130
126
|
return INTRADAY_RESOLUTIONS.reduce((best, candidate) => (
|
|
131
127
|
Math.abs(CHART_RESOLUTION_STEP_MS[candidate] - gap) < Math.abs(CHART_RESOLUTION_STEP_MS[best] - gap) ? candidate : best
|
|
132
128
|
));
|
|
133
129
|
}
|
|
134
|
-
|
|
135
|
-
return days >= 25 ? "1mo" : days >= 6 ? "1wk" : "1d";
|
|
130
|
+
return barSizeFromGap(gap);
|
|
136
131
|
}
|
|
137
132
|
|
|
138
133
|
/**
|
|
@@ -233,6 +228,25 @@ export function historyRows(
|
|
|
233
228
|
});
|
|
234
229
|
}
|
|
235
230
|
|
|
231
|
+
/**
|
|
232
|
+
* The newest weekly or monthly bar while its period has not ended at `now`
|
|
233
|
+
* (barPeriodToDate): marked `partial`, as a bar its source marks is, with
|
|
234
|
+
* how far into its period it runs, which the status line names (`month to
|
|
235
|
+
* date, through Thu 15 Oct 2026`). Daily and intraday bars are left as the
|
|
236
|
+
* source marks them. Null when the newest bar is complete.
|
|
237
|
+
*/
|
|
238
|
+
export function formingPeriodBar(
|
|
239
|
+
rows: readonly HistoryRow[],
|
|
240
|
+
venue: string | undefined,
|
|
241
|
+
now = Date.now(),
|
|
242
|
+
): { rows: HistoryRow[]; periodToDate: BarPeriodToDate } | null {
|
|
243
|
+
const last = rows.at(-1);
|
|
244
|
+
const period = last?.interval === "1mo" ? "month" : last?.interval === "1wk" ? "week" : null;
|
|
245
|
+
const forming = last && period ? barPeriodToDate(last.date, period, venue, now) : null;
|
|
246
|
+
if (!forming) return null;
|
|
247
|
+
return { rows: [...rows.slice(0, -1), { ...last!, partial: true }], periodToDate: forming };
|
|
248
|
+
}
|
|
249
|
+
|
|
236
250
|
/**
|
|
237
251
|
* The volume of a coin's history: traded value in the quote currency the
|
|
238
252
|
* prices are in. Null for anything else, whose volume stays unlabelled.
|
package/src/cli/index.ts
CHANGED
|
@@ -37,8 +37,8 @@ import { createSystemCliCommands } from "./commands/system";
|
|
|
37
37
|
import { brokerCliCommand, ibkrCliCommand } from "./commands/broker";
|
|
38
38
|
import { listPlugins, updatePlugins } from "./commands/plugins";
|
|
39
39
|
import { installListedPlugin, removePlugin } from "../plugins/installer";
|
|
40
|
-
import { catalogCommandHits, functionCodeCommand, runPaneCatalog, runPaneFunction, runPaneScreenshot } from "./pane-functions";
|
|
41
|
-
import { catalogPointer, type CatalogCommandHit } from "./unknown-command";
|
|
40
|
+
import { catalogCommandHits, functionCodeCommand, nearFunctionCodeCommand, runPaneCatalog, runPaneFunction, runPaneScreenshot } from "./pane-functions";
|
|
41
|
+
import { catalogPointer, tickerPointer, type CatalogCommandHit } from "./unknown-command";
|
|
42
42
|
import { catalogFredSeriesIds } from "./series-search";
|
|
43
43
|
|
|
44
44
|
const { width: SHOT_WIDTH, height: SHOT_HEIGHT } = SHOT_SIZE_LIMITS;
|
|
@@ -251,6 +251,12 @@ function createCoreCliCommands(
|
|
|
251
251
|
help: {
|
|
252
252
|
group: CLI_COMMAND_GROUPS.plugins,
|
|
253
253
|
usage: ["update [name]"],
|
|
254
|
+
sections: [{
|
|
255
|
+
title: "Updating Gloomberb itself",
|
|
256
|
+
lines: [
|
|
257
|
+
"The app updates itself from stable releases. config set updates.channel nightly adds the nightly build of main; see gloomberb help config.",
|
|
258
|
+
],
|
|
259
|
+
}],
|
|
254
260
|
examples: ["update", "update gloom-tv"],
|
|
255
261
|
},
|
|
256
262
|
execute: async (args) => {
|
|
@@ -317,6 +323,8 @@ function nearCommand(token: string, commands: CliCommandDef[]): string | null {
|
|
|
317
323
|
interface UnknownCommandPointers {
|
|
318
324
|
/** The word is a function's code: the command that runs it. */
|
|
319
325
|
fn: { code: string; command: string } | null;
|
|
326
|
+
/** The word is a slip from one function's code (RCDF for RDCF), read only when it is no code, no command is close to it and the catalog finds nothing for it. */
|
|
327
|
+
nearFn: { code: string; label: string; command: string } | null;
|
|
320
328
|
/** Functions the catalog finds for the word, read only when it is no code and no command is close to it. */
|
|
321
329
|
hits: CatalogCommandHit[];
|
|
322
330
|
/** What the reader typed after the word, which a pointer runs the function with. */
|
|
@@ -331,24 +339,34 @@ async function unknownCommandPointers(
|
|
|
331
339
|
commands: CliCommandDef[],
|
|
332
340
|
): Promise<UnknownCommandPointers> {
|
|
333
341
|
const fn = await functionCodeCommand(ctx, token, args);
|
|
334
|
-
const
|
|
335
|
-
|
|
342
|
+
const command = fn || nearCommand(token, commands);
|
|
343
|
+
const hits = command ? [] : await catalogCommandHits(ctx, token);
|
|
344
|
+
// What the catalog finds for a word (`comps`) outranks the code it happens to be a slip from.
|
|
345
|
+
const nearFn = command || hits.length > 0 ? null : await nearFunctionCodeCommand(ctx, token, args);
|
|
346
|
+
return { fn, nearFn, hits, args };
|
|
336
347
|
}
|
|
337
348
|
|
|
338
349
|
/**
|
|
339
350
|
* A function's code is pointed at `fn`; a typo of a command at the command;
|
|
340
|
-
* a
|
|
351
|
+
* a typo of a function's code (`RCDF`) at that function; a word the catalog
|
|
352
|
+
* knows (`comps`, `peers`) at the functions it finds. A word written as a
|
|
353
|
+
* ticker (`PLTR`) is also pointed at `quote` and `search`.
|
|
341
354
|
*/
|
|
342
355
|
function failUnknownCommand(token: string, commands: CliCommandDef[], pointers: UnknownCommandPointers): never {
|
|
343
356
|
if (pointers.fn) fail(`${pointers.fn.code} is a function: ${pointers.fn.command}`);
|
|
357
|
+
const ticker = tickerPointer(token);
|
|
344
358
|
const suggestion = nearCommand(token, commands);
|
|
359
|
+
if (!suggestion && pointers.nearFn) {
|
|
360
|
+
fail(
|
|
361
|
+
`Unknown command "${token}". Did you mean the function ${pointers.nearFn.code} (${pointers.nearFn.label})?`,
|
|
362
|
+
[`Run: ${pointers.nearFn.command}`, ticker].filter(Boolean).join("\n"),
|
|
363
|
+
);
|
|
364
|
+
}
|
|
345
365
|
const catalog = suggestion ? null : catalogPointer(token, pointers.hits, pointers.args);
|
|
346
|
-
|
|
347
|
-
`
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
: catalog ? `${catalog}\nRun gloomberb help to see every command.` : "Run gloomberb help to see every command.",
|
|
351
|
-
);
|
|
366
|
+
const help = suggestion
|
|
367
|
+
? `Did you mean ${suggestion}? Run gloomberb help to see every command.`
|
|
368
|
+
: catalog ? `${catalog}\nRun gloomberb help to see every command.` : "Run gloomberb help to see every command.";
|
|
369
|
+
fail(`Unknown command "${token}".`, ticker ? `${ticker}\n${help}` : help);
|
|
352
370
|
}
|
|
353
371
|
|
|
354
372
|
const CLI_DESCRIPTION = "Market research and portfolio tracker for the terminal";
|
|
@@ -443,7 +461,7 @@ export async function failUnknownCliCommand(args: string[], options: DispatchCli
|
|
|
443
461
|
// functions, with the arguments the reader gave.
|
|
444
462
|
const pointers = token && parsed
|
|
445
463
|
? await unknownCommandPointers(createCliCommandContext("core", registry, parsed.options, parsed.args.slice(1)), token, parsed.args.slice(1), commands)
|
|
446
|
-
: { fn: null, hits: [], args: [] };
|
|
464
|
+
: { fn: null, nearFn: null, hits: [], args: [] };
|
|
447
465
|
return failUnknownCommand(token, commands, pointers);
|
|
448
466
|
}
|
|
449
467
|
|