@gloom-sh/terminal 0.18.5 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +8 -21
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +10 -10
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +33 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +22 -4
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { METRIC_CHOICES, metricWizardFields } from "./metric-command";
|
|
2
|
-
import { metricReadings } from "./metric-data";
|
|
3
|
-
import {
|
|
1
|
+
import { METRIC_CHOICES, benchmarkKey, metricWizardFields, parseMetricAlert } from "./metric-command";
|
|
2
|
+
import { firstDailyCheck, metricAvailabilityError, metricReadings } from "./metric-data";
|
|
3
|
+
import { alertLogRule } from "./alert-log";
|
|
4
|
+
import { closeBasis, isDailyMetric, isRelativeMetric, metricReadingText, metricTrigger, validMetricRule, type MetricReading } from "./metric-model";
|
|
5
|
+
import { canonicalExchange } from "../../../utils/exchanges";
|
|
4
6
|
import { startCatalystAlerts } from "./catalysts";
|
|
5
7
|
import { notTheCoinNote } from "../../../cli/commands/crypto-hints";
|
|
6
|
-
import { createFiredAlertRecorder, firedAlertTime, quoteSourceWords } from "./fired-log";
|
|
8
|
+
import { alertChangeEntry, createFiredAlertRecorder, firedAlertTime, quoteSourceWords } from "./fired-log";
|
|
7
9
|
import { checkIvLevelAlertsNow, startIvLevelAlerts } from "./iv-level";
|
|
8
10
|
import { logFailure } from "../../../utils/log-failure";
|
|
9
11
|
import { RESEARCH_FORM_KINDS, RESEARCH_LABELS, researchWizardFields, createResearchAlert } from "./research-builder";
|
|
@@ -22,12 +24,15 @@ import { isResearchAlertKind, MAX_LEAD_DAYS } from "./research-rules";
|
|
|
22
24
|
import { EVENT_ALERT_TEMPLATES, MAX_EVENT_ALERTS, createEventAlert, eventAlertStoreKey, readEventAlertStores, updateEventAlerts } from "./events";
|
|
23
25
|
import { formatMarketPrice } from "../../../market-data/market/format";
|
|
24
26
|
import { settleTypedListingKey } from "../../../tickers/listing-venues";
|
|
27
|
+
import { parseListingArg } from "../../../cli/listing-arg";
|
|
28
|
+
import { notATickerSymbol } from "../../../sources/provider-errors";
|
|
25
29
|
import type { Quote } from "../../../types/financials";
|
|
26
30
|
import type { GloomPlugin } from "../../../types/plugin";
|
|
27
31
|
import {
|
|
28
32
|
createAlert,
|
|
29
33
|
evaluateAlert,
|
|
30
34
|
formatAlertDescription,
|
|
35
|
+
sameLevelRule,
|
|
31
36
|
} from "./alert-engine";
|
|
32
37
|
import {
|
|
33
38
|
parseAlertCommandValues,
|
|
@@ -65,6 +70,16 @@ let alertStream: AlertQuoteStream | null = null;
|
|
|
65
70
|
*/
|
|
66
71
|
const STREAM_PERSIST_MS = 60_000;
|
|
67
72
|
|
|
73
|
+
/** BTC/USD, BRK/B and EUR/USD are one listing written with a slash, as `alerts add` reads them; XLK/SPY is not. */
|
|
74
|
+
function slashSpellsListing(symbol: string): boolean {
|
|
75
|
+
try {
|
|
76
|
+
return parseListingArg(symbol).key !== symbol.toUpperCase();
|
|
77
|
+
} catch {
|
|
78
|
+
// Intentional ignore: text the listing reader refuses is no pair to divide, so the quote's own error stands.
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
68
83
|
export const alertsPlugin: GloomPlugin = {
|
|
69
84
|
id: "alerts",
|
|
70
85
|
name: "Alerts",
|
|
@@ -186,31 +201,75 @@ export const alertsPlugin: GloomPlugin = {
|
|
|
186
201
|
...metricWizardFields,
|
|
187
202
|
],
|
|
188
203
|
async execute(values) {
|
|
189
|
-
|
|
204
|
+
let input = parseAlertCommandValues(values);
|
|
190
205
|
if (!input) throw new Error("Use a symbol, condition, and target price.");
|
|
191
206
|
// 7203:TSE, typed or from a link, is the venue its symbol trades on, as `alerts add` reads it.
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
207
|
+
const quoteFor = async (typed: string) => {
|
|
208
|
+
const key = (await settleTypedListingKey(typed, { store: ctx.tickerRepository, dataProvider: ctx.marketData })).toUpperCase();
|
|
209
|
+
return { key, quote: await resolveAlertQuote(ctx.marketData, key) };
|
|
210
|
+
};
|
|
211
|
+
let resolved: Awaited<ReturnType<typeof quoteFor>>;
|
|
212
|
+
try {
|
|
213
|
+
resolved = await quoteFor(input.symbol);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
// XLK/SPY is a listing first; only one the service says is no ticker is the ratio of the two, as `alerts add` reads it.
|
|
216
|
+
// A timeout stays a timeout, and BTC/USD, BRK/B or EUR/USD, one listing written with a slash, keeps its own answer.
|
|
217
|
+
const pair = !input.metric && /^[^/\s]+\/[^/\s]+$/.test(input.symbol) && notATickerSymbol(error instanceof Error ? error.message : String(error))
|
|
218
|
+
&& !slashSpellsListing(input.symbol) ? parseMetricAlert(`${input.symbol} ratio ${input.condition} ${input.price}`) : null;
|
|
219
|
+
if (!pair) throw error;
|
|
220
|
+
input = pair;
|
|
221
|
+
resolved = await quoteFor(pair.symbol);
|
|
222
|
+
}
|
|
223
|
+
const { key: symbol, quote } = resolved;
|
|
224
|
+
let metric = input.metric;
|
|
225
|
+
if (metric && isRelativeMetric(metric)) {
|
|
226
|
+
const reference = (await quoteFor(benchmarkKey(metric))).quote;
|
|
227
|
+
const benchmarkExchange = canonicalExchange(reference.listingExchangeName || reference.exchangeName || "");
|
|
228
|
+
const exchange = canonicalExchange(quote.listingExchangeName || quote.exchangeName || "");
|
|
229
|
+
const benchmark = reference.symbol || metric.benchmark;
|
|
230
|
+
if (benchmark === (quote.symbol || symbol) && benchmarkExchange === exchange) throw new Error(`Compare ${benchmark} with another listing.`);
|
|
231
|
+
metric = { ...metric, benchmark, ...(benchmarkExchange ? { benchmarkExchange } : {}) };
|
|
232
|
+
// Never save a rule its own list would refuse to read back.
|
|
233
|
+
if (!validMetricRule({ metric, condition: input.condition, targetPrice: input.price })) throw new Error(`${benchmark} cannot be a benchmark: relative strength alerts compare two US listings.`);
|
|
234
|
+
}
|
|
195
235
|
// A target ten times away is a slip or another instrument's price, such as bitcoin's on the BTC fund.
|
|
196
236
|
const far = farTargetMessage(symbol, input.price, quote.price, symbol.includes(":") ? null : notTheCoinNote(symbol, quote));
|
|
197
237
|
if (!input.metric && far) throw new Error(far);
|
|
198
238
|
|
|
199
|
-
const alert = {
|
|
239
|
+
const alert: AlertRule = {
|
|
200
240
|
...createAlert(quote.symbol || symbol, input.condition, input.price),
|
|
201
|
-
...(!
|
|
241
|
+
...(!metric ? quoteAlertFields(quote) : {
|
|
242
|
+
metric, exchange: quote.listingExchangeName || quote.exchangeName,
|
|
243
|
+
...quote.currency ? { lastQuoteCurrency: quote.currency } : {},
|
|
244
|
+
}),
|
|
202
245
|
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
246
|
+
// A close not yet final is read once it is: the rule is kept and says when it is first judged.
|
|
247
|
+
let firstCheck: string | null = null;
|
|
248
|
+
if (metric) {
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
if (isDailyMetric(metric)) {
|
|
251
|
+
const check = firstDailyCheck(alert.exchange, now, metric.kind === "rrg");
|
|
252
|
+
if (check) firstCheck = `first checked on the ${check.day} ${closeBasis(metric) === "weekly close" ? "weekly close" : "close"}, from ${new Date(check.at).toISOString().slice(11, 16)} UTC`;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const reading = await metricReadings(ctx.marketData, now)(alert);
|
|
256
|
+
Object.assign(alert, { lastCheckedPrice: reading.value, lastCheckedTarget: reading.target, lastQuoteUpdatedAt: reading.at,
|
|
257
|
+
lastCheckedAt: now, ...reading.day ? { lastDailyClose: reading.day } : {} });
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (!isDailyMetric(metric) || metricAvailabilityError(alert)) throw error;
|
|
260
|
+
}
|
|
206
261
|
}
|
|
207
262
|
const existing = loadAlerts(ctx);
|
|
263
|
+
// The same rule on the same listing is one alert: adding it again names it and adds nothing.
|
|
264
|
+
const duplicate = existing.find((entry) => sameLevelRule(entry, alert));
|
|
265
|
+
if (duplicate) throw new Error(`${formatAlertDescription(alert)} already exists (alert ${duplicate.id}). Nothing was added.`);
|
|
208
266
|
existing.push(alert);
|
|
209
267
|
await saveAlerts(ctx, existing, input.metric ? "metric" : "price");
|
|
268
|
+
recordFired(alertChangeEntry("created", alertLogRule(alert)));
|
|
210
269
|
alertStream?.sync();
|
|
211
270
|
|
|
212
271
|
ctx.notify({
|
|
213
|
-
body: `Alert set: ${formatAlertDescription(alert)}${input.metric ? "" : ` (current ${formatMarketPrice(quote.price, { minimumFractionDigits: 2 })})`}`,
|
|
272
|
+
body: `Alert set: ${formatAlertDescription(alert)}${input.metric ? firstCheck ? `, ${firstCheck}` : "" : ` (current ${formatMarketPrice(quote.price, { minimumFractionDigits: 2 })})`}`,
|
|
214
273
|
type: "success",
|
|
215
274
|
});
|
|
216
275
|
},
|
|
@@ -281,6 +340,7 @@ export const alertsPlugin: GloomPlugin = {
|
|
|
281
340
|
// An IV level goes in its own list, which released versions never read.
|
|
282
341
|
const key = eventAlertStoreKey(rule);
|
|
283
342
|
await ctx.configState.set(key, updateEventAlerts(ctx.configState.get<string>(key), (rules) => [...rules, rule]));
|
|
343
|
+
recordFired(alertChangeEntry("created", alertLogRule(rule)));
|
|
284
344
|
ctx.notify({ body: "Event alert saved.", type: "success" });
|
|
285
345
|
if (scheduled) checkScheduledEventAlertsNow();
|
|
286
346
|
if (rule.kind === "iv_level") checkIvLevelAlertsNow();
|
|
@@ -335,11 +395,14 @@ export const alertsPlugin: GloomPlugin = {
|
|
|
335
395
|
const firedAt = Date.now();
|
|
336
396
|
alert.status = "triggered"; alert.triggeredAt = firedAt;
|
|
337
397
|
const description = formatAlertDescription(alert);
|
|
398
|
+
const readingText = metricReadingText(alert.metric, reading);
|
|
338
399
|
recordFired({ kind: alert.metric.kind, ruleId: alert.id, rule: description, firedAt, value: reading.value,
|
|
339
400
|
unit: alert.metric.kind === "pe" || alert.metric.kind === "ps" ? "x" : undefined,
|
|
340
|
-
|
|
401
|
+
// An RRG quadrant is two readings; the figure is strength, the detail says both.
|
|
402
|
+
...(alert.metric.kind === "rrg" ? { detail: readingText } : {}),
|
|
403
|
+
quoteAt: firedAlertTime(reading.at), source: closeBasis(alert.metric),
|
|
341
404
|
symbol: alert.symbol, exchange: alert.exchange });
|
|
342
|
-
ctx.notify({ body: `${description} triggered at ${
|
|
405
|
+
ctx.notify({ body: alert.metric.kind === "rrg" ? `${description} triggered: ${readingText}` : `${description} triggered at ${readingText}`, type: "success",
|
|
343
406
|
refId: `price:${alert.id}`, desktop: "always", persistent: true, sound: "Glass",
|
|
344
407
|
action: { label: "Open", onClick: () => ctx.showPane("alerts") } });
|
|
345
408
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { AlertMetric } from "./metric-model";
|
|
2
|
-
import { validMetricRule } from "./metric-model";
|
|
1
|
+
import type { AlertMetric, RotationQuadrant } from "./metric-model";
|
|
2
|
+
import { ROTATION_QUADRANTS, validMetricRule } from "./metric-model";
|
|
3
3
|
import { readAlertPrice } from "./price";
|
|
4
4
|
import type { AlertCondition } from "./types";
|
|
5
5
|
import type { WizardStep } from "../../../types/plugin";
|
|
6
|
+
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
6
7
|
|
|
7
8
|
export interface MetricAlertInput { symbol: string; condition: AlertCondition; price: number; metric: AlertMetric }
|
|
8
9
|
const direction = (s: string) => /^(above|over|>)$/i.test(s) ? "above" as const : /^(below|under|<)$/i.test(s) ? "below" as const : null;
|
|
@@ -11,10 +12,58 @@ function checked(input: MetricAlertInput): MetricAlertInput {
|
|
|
11
12
|
if (!validMetricRule({ ...input, targetPrice: input.price })) throw new Error("Use a positive multiple, RSI from 0 to 100, and whole MA/RSI periods from 2 to 200 (fast MA shorter than slow MA).");
|
|
12
13
|
return input;
|
|
13
14
|
}
|
|
15
|
+
// RRG's own default benchmark (relative-rotation/model.ts), kept here so the CLI does not load the RRG pane's model.
|
|
16
|
+
const DEFAULT_ROTATION_BENCHMARK = "SPY:NYSEARCA";
|
|
17
|
+
const RELATIVE_USAGE = "Relative strength alerts take the ratio of two listings or an RRG quadrant: alerts add XLK/SPY above 0.25, or alerts add XLE rrg leading [--benchmark SPY].";
|
|
18
|
+
const QUADRANT_USAGE = "An RRG alert names the quadrant it enters: leading, weakening, lagging or improving, such as alerts add XLE rrg leading [--benchmark SPY].";
|
|
19
|
+
/** A benchmark typed as SPY or SPY:NYSEARCA; its listing is confirmed when the alert is added. */
|
|
20
|
+
function benchmarkListing(text: string): { benchmark: string; benchmarkExchange?: string } {
|
|
21
|
+
const parsed = parsePublicTickerKey(text.toUpperCase());
|
|
22
|
+
if (!/^[A-Z0-9.^=-]{1,20}$/.test(parsed.symbol)) throw new Error(`Not a benchmark listing: ${text}.`);
|
|
23
|
+
return { benchmark: parsed.symbol, ...(parsed.exchange ? { benchmarkExchange: parsed.exchange } : {}) };
|
|
24
|
+
}
|
|
25
|
+
const sameListing = (symbol: string, metric: { benchmark: string }) => parsePublicTickerKey(symbol).symbol === metric.benchmark;
|
|
26
|
+
/**
|
|
27
|
+
* `XLK/SPY ratio above 0.25` and `XLE rrg leading [vs SPY]`; undefined for anything else. `XLK/SPY above 0.25`
|
|
28
|
+
* without the word is first a listing (BRK/B, EUR/USD), so `alerts add` reads it as a ratio only once it quotes nothing.
|
|
29
|
+
*/
|
|
30
|
+
function parseRelativeAlert(parts: readonly string[]): MetricAlertInput | undefined {
|
|
31
|
+
const [first = "", second = "", ...rest] = parts;
|
|
32
|
+
const kind = second.toLowerCase();
|
|
33
|
+
if (kind === "rrg") {
|
|
34
|
+
const [quadrant = "", vs, benchmark, ...extra] = rest;
|
|
35
|
+
if (first.includes("/")) throw new Error(`Name the benchmark of an RRG alert after it: alerts add ${first.split("/")[0]!.toUpperCase()} rrg ${quadrant.toLowerCase() || "leading"} --benchmark ${first.split("/")[1]!.toUpperCase()}.`);
|
|
36
|
+
if (!(ROTATION_QUADRANTS as readonly string[]).includes(quadrant.toLowerCase())) throw new Error(QUADRANT_USAGE);
|
|
37
|
+
if (vs !== undefined && (!/^(vs\.?|versus|against)$/i.test(vs) || !benchmark || extra.length > 0)) throw new Error(QUADRANT_USAGE);
|
|
38
|
+
const metric = { kind: "rrg" as const, quadrant: quadrant.toLowerCase() as RotationQuadrant, ...benchmarkListing(benchmark ?? DEFAULT_ROTATION_BENCHMARK) };
|
|
39
|
+
if (sameListing(first, metric)) throw new Error(`An RRG alert compares ${first.toUpperCase()} with another listing: alerts add ${first.toUpperCase()} rrg ${metric.quadrant} --benchmark SPY.`);
|
|
40
|
+
return checked({ symbol: first.toUpperCase(), condition: "crosses", price: 0, metric });
|
|
41
|
+
}
|
|
42
|
+
const pair = /^([^/]+)\/([^/]+)$/.exec(first);
|
|
43
|
+
if (pair && kind === "ratio") {
|
|
44
|
+
const [typed = "", level, ...extra] = rest;
|
|
45
|
+
const condition = direction(typed) ?? (/^(crosses|cross|x)$/i.test(typed) ? "crosses" as const : null);
|
|
46
|
+
const example = `alerts add ${first.toUpperCase()} above 0.25`;
|
|
47
|
+
if (!condition || level === undefined || extra.length > 0) throw new Error(`A ratio alert takes above, below or crosses and a ratio: ${example}.`);
|
|
48
|
+
const metric = { kind: "rs_ratio" as const, ...benchmarkListing(pair[2]!) };
|
|
49
|
+
if (sameListing(pair[1]!, metric)) throw new Error(`A ratio alert divides one listing by another: ${example}.`);
|
|
50
|
+
const price = readAlertPrice(level);
|
|
51
|
+
if (!(price > 0)) throw new Error(`A ratio alert takes a positive ratio: ${example}.`);
|
|
52
|
+
return checked({ symbol: pair[1]!.toUpperCase(), condition, price, metric });
|
|
53
|
+
}
|
|
54
|
+
// `XLE rs above 100`, `XLE vs SPY above 1`, `XLE relative strength leading`: a relative alert typed another way.
|
|
55
|
+
if (/^(rs|rsr|rs-?ratio|rs_ratio|rs-?momentum|rel|relative|rotation|quadrant|ratio|vs\.?|versus)$/i.test(kind)
|
|
56
|
+
&& rest.some((word) => /^(above|below|over|under|crosses|cross|>|<|leading|weakening|lagging|improving|rrg)$/i.test(word))) {
|
|
57
|
+
throw new Error(RELATIVE_USAGE);
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
14
61
|
/** Parses both compact CLI notation and the words traders type, without swallowing malformed metrics as calendar events. */
|
|
15
62
|
export function parseMetricAlert(input: string): MetricAlertInput | null {
|
|
16
63
|
const words = input.trim().replace(/\s+/g, " ");
|
|
17
64
|
const parts = words.split(" ");
|
|
65
|
+
const relative = parseRelativeAlert(parts);
|
|
66
|
+
if (relative) return relative;
|
|
18
67
|
if (!/^(p\/?e|p\/?s|rsi(?:\(|$)|ma(?:\(|$)|\d+-day|closes?$)/i.test(parts[1] ?? "")) return null;
|
|
19
68
|
const ratio = /^(\S+) (p\/?e|p\/?s) (above|below|over|under|>|<) (\S+)$/i.exec(words);
|
|
20
69
|
if (ratio) return checked({ symbol: ratio[1]!.toUpperCase(), metric: { kind: ratio[2]!.toLowerCase().replace("/", "") as "pe" | "ps" }, condition: direction(ratio[3]!)!, price: readAlertPrice(ratio[4]!) });
|
|
@@ -27,8 +76,17 @@ export function parseMetricAlert(input: string): MetricAlertInput | null {
|
|
|
27
76
|
if (close) return checked({ symbol: close[1]!.toUpperCase(), metric: { kind: "close_ma", period: Number(close[3]) }, condition: direction(close[2]!)!, price: 0 });
|
|
28
77
|
throw new Error(USAGE);
|
|
29
78
|
}
|
|
79
|
+
export const benchmarkKey = (metric: { benchmark: string; benchmarkExchange?: string }) => `${metric.benchmark}${metric.benchmarkExchange ? `:${metric.benchmarkExchange}` : ""}`;
|
|
80
|
+
/** A relative alert as `alerts add` takes it, for editing: `XLK/SPY ratio above 0.25`, `XLE rrg leading vs SPY`. */
|
|
81
|
+
export function relativeAlertText(input: { symbol: string; condition: AlertCondition; targetPrice: number; metric?: AlertMetric }): string | null {
|
|
82
|
+
const m = input.metric;
|
|
83
|
+
if (m?.kind === "rs_ratio") return `${input.symbol}/${m.benchmark} ratio ${input.condition} ${input.targetPrice}`;
|
|
84
|
+
return m?.kind === "rrg" ? `${input.symbol} rrg ${m.quadrant} vs ${m.benchmark}` : null;
|
|
85
|
+
}
|
|
30
86
|
export function metricShortcut(input: MetricAlertInput): Record<string, string> {
|
|
31
87
|
const m = input.metric;
|
|
88
|
+
if (m.kind === "rs_ratio") return { symbol: input.symbol, kind: m.kind, "rs_ratio:benchmark": benchmarkKey(m), "rs_ratio:condition": input.condition, "rs_ratio:target": String(input.price) };
|
|
89
|
+
if (m.kind === "rrg") return { symbol: input.symbol, kind: m.kind, "rrg:quadrant": m.quadrant, "rrg:benchmark": benchmarkKey(m) };
|
|
32
90
|
return { symbol: input.symbol, kind: m.kind, [`${m.kind}:condition`]: m.kind === "ma_cross" ? m.direction : input.condition,
|
|
33
91
|
[`${m.kind}:target`]: String(input.price), ...("period" in m ? { [`${m.kind}:period`]: String(m.period) } : {}),
|
|
34
92
|
...(m.kind === "ma_cross" ? { "ma_cross:slowPeriod": String(m.slowPeriod) } : {}) };
|
|
@@ -38,6 +96,11 @@ export function metricFormInput(values: Record<string, string>): MetricAlertInpu
|
|
|
38
96
|
if (!kind || kind === "price") return null;
|
|
39
97
|
const symbol = values.symbol?.trim().toUpperCase();
|
|
40
98
|
if (!symbol) return null;
|
|
99
|
+
if (kind === "rs_ratio") {
|
|
100
|
+
const benchmark = values["rs_ratio:benchmark"]?.trim();
|
|
101
|
+
return benchmark ? parseMetricAlert(`${symbol}/${benchmark} ratio ${values["rs_ratio:condition"] ?? "above"} ${values["rs_ratio:target"] ?? ""}`) : null;
|
|
102
|
+
}
|
|
103
|
+
if (kind === "rrg") return parseMetricAlert(`${symbol} rrg ${values["rrg:quadrant"] ?? "leading"} vs ${values["rrg:benchmark"]?.trim() || DEFAULT_ROTATION_BENCHMARK}`);
|
|
41
104
|
const condition = values[`${kind}:condition`] ?? "below";
|
|
42
105
|
const window = values[`${kind}:period`] ?? (kind === "rsi" ? "14" : kind === "ma_cross" ? "50" : "200");
|
|
43
106
|
const text = kind === "ma_cross" ? `${symbol} ma(${window}) crosses ${condition} ma(${values["ma_cross:slowPeriod"] ?? "200"})`
|
|
@@ -48,8 +111,18 @@ export function metricFormInput(values: Record<string, string>): MetricAlertInpu
|
|
|
48
111
|
export const METRIC_CHOICES = [
|
|
49
112
|
{ value: "price", label: "Price" }, { value: "pe", label: "P/E (TTM)" }, { value: "ps", label: "P/S (TTM)" },
|
|
50
113
|
{ value: "rsi", label: "RSI" }, { value: "ma_cross", label: "MA crossover" }, { value: "close_ma", label: "Close vs MA" },
|
|
114
|
+
{ value: "rs_ratio", label: "Ratio to benchmark" }, { value: "rrg", label: "RRG quadrant" },
|
|
115
|
+
];
|
|
116
|
+
const RELATIVE_FIELDS: WizardStep[] = [
|
|
117
|
+
{ key: "rs_ratio:benchmark", label: "Divided by", type: "text", placeholder: "SPY", dependsOn: { key: "kind", value: "rs_ratio" } },
|
|
118
|
+
{ key: "rs_ratio:condition", label: "Condition", type: "select", defaultValue: "above", dependsOn: { key: "kind", value: "rs_ratio" },
|
|
119
|
+
options: [{ label: "Above", value: "above" }, { label: "Below", value: "below" }, { label: "Crosses", value: "crosses" }] },
|
|
120
|
+
{ key: "rs_ratio:target", label: "Ratio", type: "number", placeholder: "0.25", dependsOn: { key: "kind", value: "rs_ratio" } },
|
|
121
|
+
{ key: "rrg:quadrant", label: "Enters", type: "select", defaultValue: "leading", dependsOn: { key: "kind", value: "rrg" },
|
|
122
|
+
options: ROTATION_QUADRANTS.map((value) => ({ label: `${value[0]!.toUpperCase()}${value.slice(1)}`, value })) },
|
|
123
|
+
{ key: "rrg:benchmark", label: "Benchmark", type: "text", defaultValue: "SPY", placeholder: "SPY", dependsOn: { key: "kind", value: "rrg" } },
|
|
51
124
|
];
|
|
52
|
-
export const metricWizardFields: WizardStep[] = METRIC_CHOICES.filter(x => x.value !== "price").flatMap(({ value: kind }) => {
|
|
125
|
+
export const metricWizardFields: WizardStep[] = [...METRIC_CHOICES.filter(x => x.value !== "price" && x.value !== "rs_ratio" && x.value !== "rrg").flatMap(({ value: kind }) => {
|
|
53
126
|
const dependsOn = { key: "kind", value: kind };
|
|
54
127
|
return [
|
|
55
128
|
...(["rsi", "ma_cross", "close_ma"].includes(kind) ? [{ key: `${kind}:period`, label: kind === "ma_cross" ? "Fast MA (days)" : kind === "rsi" ? "RSI period" : "MA (days)", type: "number" as const, defaultValue: kind === "rsi" ? "14" : kind === "ma_cross" ? "50" : "200", dependsOn }] : []),
|
|
@@ -58,4 +131,4 @@ export const metricWizardFields: WizardStep[] = METRIC_CHOICES.filter(x => x.val
|
|
|
58
131
|
...(kind === "ma_cross" ? [{ key: "ma_cross:slowPeriod", label: "Slow MA (days)", type: "number" as const, defaultValue: "200", dependsOn }]
|
|
59
132
|
: kind === "close_ma" ? [] : [{ key: `${kind}:target`, label: kind === "rsi" ? "RSI level" : "Target multiple", type: "number" as const, placeholder: kind === "rsi" ? "30" : kind === "pe" ? "25" : "8", dependsOn }]),
|
|
60
133
|
];
|
|
61
|
-
});
|
|
134
|
+
}), ...RELATIVE_FIELDS];
|
|
@@ -5,7 +5,9 @@ import { comparablePriceEarnings } from "../../../utils/price-earnings";
|
|
|
5
5
|
import { canonicalExchange } from "../../../utils/exchanges";
|
|
6
6
|
import { getPublishedUsEquitySession } from "../../../market-data/published-us-sessions";
|
|
7
7
|
import { barSessionDay } from "../../../market-data/market/period-return";
|
|
8
|
-
import {
|
|
8
|
+
import { finalDailyBars } from "../../../time-series/session-bar";
|
|
9
|
+
import { getPricePointTimestamp } from "../../../utils/price-history";
|
|
10
|
+
import { DAILY_CLOSE_SETTLE_MS, dailyMetricReading, latestSettledClose, DailyMetricCache, isDailyMetric, isRelativeMetric, type MetricReading } from "./metric-model";
|
|
9
11
|
import type { AlertRule } from "./types";
|
|
10
12
|
|
|
11
13
|
// Keep valuation fixtures in step with the delivery evaluator and the research panes.
|
|
@@ -21,12 +23,43 @@ export function valuationAlertValue(kind: "pe" | "ps", data: TickerFinancials):
|
|
|
21
23
|
return Number.isFinite(value) && value > 0 ? value : null;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
const US_CALENDAR = "Daily technical alerts need a published US listing calendar; delivery is unavailable for this listing.";
|
|
27
|
+
/** Why a rule's listings cannot be judged on settled closes: a benchmark is held to the same US calendar as the listing. */
|
|
28
|
+
export function metricAvailabilityError(rule: AlertRule, now = Date.now()): string | null {
|
|
25
29
|
if (!rule.metric || !isDailyMetric(rule.metric)) return null;
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
const today = new Date(now).toISOString().slice(0, 10);
|
|
31
|
+
if (!getPublishedUsEquitySession(canonicalExchange(rule.exchange ?? ""), today)) return US_CALENDAR;
|
|
32
|
+
return isRelativeMetric(rule.metric) && !getPublishedUsEquitySession(canonicalExchange(rule.metric.benchmarkExchange ?? ""), today)
|
|
33
|
+
? `Relative strength alerts compare two US listings; ${rule.metric.benchmark} has no published US listing calendar here.` : null;
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The first close a daily technical rule set at `now` is judged on, and when:
|
|
38
|
+
* the next session to close after it, read once the close has settled
|
|
39
|
+
* (DAILY_CLOSE_SETTLE_MS). `weekly` (an RRG quadrant) waits for the last
|
|
40
|
+
* session of that Monday-Friday week, its weekly close. Null when the listing
|
|
41
|
+
* has no published calendar.
|
|
42
|
+
*/
|
|
43
|
+
export function firstDailyCheck(exchange: string | undefined, now = Date.now(), weekly = false): { day: string; at: number } | null {
|
|
44
|
+
const venue = canonicalExchange(exchange ?? "");
|
|
45
|
+
let first: { day: string; close: number } | null = null;
|
|
46
|
+
for (let offset = 0; offset < 15; offset++) {
|
|
47
|
+
const time = now + offset * 86_400_000;
|
|
48
|
+
const day = new Date(time).toISOString().slice(0, 10);
|
|
49
|
+
// The weekly close is the last session before the weekend after the first close.
|
|
50
|
+
if (first && new Date(time).getUTCDay() === 6) break;
|
|
51
|
+
const session = getPublishedUsEquitySession(venue, day);
|
|
52
|
+
if (!session) return null;
|
|
53
|
+
if (session.kind !== "session" || session.close <= now) continue;
|
|
54
|
+
first = { day, close: session.close };
|
|
55
|
+
if (!weekly) break;
|
|
56
|
+
}
|
|
57
|
+
return first ? { day: first.day, at: first.close + DAILY_CLOSE_SETTLE_MS } : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The bar size the daily rules read, the one `history` and the charts ask for. */
|
|
61
|
+
const DAILY_BARS = "1d";
|
|
62
|
+
|
|
30
63
|
const dailyCaches = new WeakMap<AssetDataProvider, DailyMetricCache>();
|
|
31
64
|
|
|
32
65
|
/** A pass shares each listing's reads; there is no second provider or quote subscription. */
|
|
@@ -48,23 +81,31 @@ export function metricReadings(provider: AssetDataProvider, now = Date.now()) {
|
|
|
48
81
|
}
|
|
49
82
|
const exchange = canonicalExchange(rule.exchange ?? "");
|
|
50
83
|
const session = (day: string) => getPublishedUsEquitySession(exchange, day);
|
|
51
|
-
|
|
84
|
+
const unavailable = metricAvailabilityError(rule, now);
|
|
85
|
+
if (unavailable) throw new Error(unavailable);
|
|
52
86
|
const latest = latestSettledClose(now, session);
|
|
53
87
|
const ruleKey = JSON.stringify([rule.id, rule.armedAt ?? rule.createdAt, key, rule.condition, rule.targetPrice, m]);
|
|
54
88
|
return daily.read(ruleKey, latest.day, now, async () => {
|
|
55
|
-
const load = async () => {
|
|
89
|
+
const load = async (symbol: string, venue: string) => {
|
|
56
90
|
// An explicit end at the settled session avoids today's forming-bar quote read.
|
|
57
91
|
const end = new Date(`${latest.day}T23:59:59Z`);
|
|
58
92
|
const start = new Date(Date.parse(latest.day) - 366 * 86_400_000);
|
|
59
|
-
if (provider.getDetailedPriceHistoryWithMetadata) return (await provider.getDetailedPriceHistoryWithMetadata(
|
|
60
|
-
if (provider.getDetailedPriceHistory) return provider.getDetailedPriceHistory(
|
|
93
|
+
if (provider.getDetailedPriceHistoryWithMetadata) return (await provider.getDetailedPriceHistoryWithMetadata(symbol, venue, start, end, DAILY_BARS, { cacheMode: "current" })).points;
|
|
94
|
+
if (provider.getDetailedPriceHistory) return provider.getDetailedPriceHistory(symbol, venue, start, end, DAILY_BARS, { cacheMode: "current" });
|
|
61
95
|
throw new Error("Completed daily history is unavailable from this connection.");
|
|
62
96
|
};
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
97
|
+
const completed = async (symbol: string, venue: string) => {
|
|
98
|
+
const listing = `${symbol}|${venue}`;
|
|
99
|
+
const request = history.get(listing) ?? load(symbol, venue);
|
|
100
|
+
history.set(listing, request);
|
|
101
|
+
// Final as `history` shows it: a close it prints without `partial` is one the rule may judge.
|
|
102
|
+
// Bars read back from the saved cache carry their dates as text.
|
|
103
|
+
return finalDailyBars(await request, { now, venue }).map(point => ({ date: barSessionDay(getPricePointTimestamp(point), venue), close: point.close }));
|
|
104
|
+
};
|
|
105
|
+
const bars = await completed(rule.symbol, exchange);
|
|
106
|
+
// RS ratios and RRG quadrants read the benchmark's own completed bars, never a quote.
|
|
107
|
+
const benchmark = isRelativeMetric(m) ? await completed(m.benchmark, canonicalExchange(m.benchmarkExchange ?? "")) : undefined;
|
|
108
|
+
return dailyMetricReading(rule, bars, now, session, benchmark);
|
|
68
109
|
});
|
|
69
110
|
};
|
|
70
111
|
}
|