gloomberb 0.18.3 → 0.18.4
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 +8 -8
- package/README.zh-CN.md +11 -11
- package/bin/{gloomberb → gloom} +3 -1
- package/package.json +6 -2
- package/src/account/checkout-handoff.tsx +225 -0
- package/src/account/device-signin-dialog.tsx +2 -2
- package/src/account/device-signin.ts +2 -2
- package/src/account/upgrade-dialog.tsx +1 -1
- package/src/api-client/askg.ts +2 -2
- package/src/api-client/auth.ts +2 -2
- package/src/api-client/central-bank-rates.ts +6 -0
- package/src/api-client/collections.ts +5 -1
- package/src/api-client/cot.ts +2 -0
- package/src/api-client/crypto-etf-flows.ts +37 -0
- package/src/api-client/crypto-markets.ts +60 -0
- package/src/api-client/data.ts +84 -5
- package/src/api-client/doe.ts +15 -0
- package/src/api-client/equity-screener.ts +28 -0
- package/src/api-client/exchange-announcements.ts +38 -0
- package/src/api-client/fund-facts.ts +39 -0
- package/src/api-client/futures-curve.ts +9 -1
- package/src/api-client/hiring.ts +301 -0
- package/src/api-client/index.ts +22 -5
- package/src/api-client/market-discovery.ts +14 -0
- package/src/api-client/members.ts +6 -0
- package/src/api-client/mna.ts +6 -0
- package/src/api-client/paths.ts +18 -1
- package/src/api-client/request.ts +21 -4
- package/src/api-client/short-side-publications.ts +37 -0
- package/src/api-client/socket.ts +64 -4
- package/src/api-client/swap-curve.ts +35 -0
- package/src/api-client/themes.ts +7 -1
- package/src/api-client/types.ts +90 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -2
- package/src/app/runtime/desktop-deeplink.ts +5 -5
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +13 -0
- package/src/app/star-prompt/status-line.tsx +2 -2
- package/src/architecture/core-builtin-allowlist.json +5 -2
- package/src/architecture/ui-literals-allowlist.json +3 -3
- package/src/architecture/ui-tokens-allowlist.json +1 -1
- package/src/brokers/broker-portfolio-sync.ts +37 -0
- package/src/brokers/directory.ts +11 -0
- package/src/brokers/logos.ts +3 -1
- package/src/brokers/signed-in/adapter.ts +304 -26
- package/src/brokers/signed-in/client.ts +14 -2
- package/src/brokers/signed-in/connect.ts +10 -2
- package/src/brokers/sync-broker-instance.ts +9 -1
- package/src/capabilities/chart-series.ts +22 -6
- package/src/cli/bank-section.ts +1 -1
- package/src/cli/base-converter.ts +23 -14
- package/src/cli/central-bank-events.ts +1 -1
- package/src/cli/command-options.ts +59 -4
- package/src/cli/commands/api.ts +9 -6
- package/src/cli/commands/broker.ts +2 -2
- package/src/cli/commands/command-utils.ts +2 -2
- package/src/cli/commands/crypto-hints.ts +7 -20
- package/src/cli/commands/fred-series.ts +4 -2
- package/src/cli/commands/keybindings.ts +2 -2
- package/src/cli/commands/market.ts +1117 -264
- package/src/cli/commands/news-hints.ts +2 -2
- package/src/cli/commands/news-search.ts +59 -6
- package/src/cli/commands/options-leaps.ts +41 -26
- package/src/cli/commands/overview.ts +121 -48
- package/src/cli/commands/plugins.ts +9 -9
- package/src/cli/commands/remote.ts +13 -10
- package/src/cli/commands/search.ts +64 -44
- package/src/cli/commands/system.ts +27 -19
- package/src/cli/commands/themes.ts +1 -1
- package/src/cli/commands/ticker.ts +192 -47
- package/src/cli/context.ts +3 -2
- package/src/cli/desktop-pane-shot-view.ts +2 -2
- package/src/cli/desktop-pane-shot.ts +1 -1
- package/src/cli/earnings-calendar.ts +4 -4
- package/src/cli/entry.ts +18 -1
- package/src/cli/errors.ts +20 -2
- package/src/cli/exit-codes.ts +1 -1
- package/src/cli/export-source.ts +10 -4
- package/src/cli/freshness.ts +2 -8
- package/src/cli/fx-forward.ts +130 -0
- package/src/cli/fx-pair.ts +10 -9
- package/src/cli/glossary.ts +64 -20
- package/src/cli/help.ts +19 -13
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +196 -43
- package/src/cli/index-roots.ts +5 -45
- package/src/cli/index.ts +24 -18
- package/src/cli/known-gaps.ts +118 -25
- package/src/cli/listing-arg.ts +300 -41
- package/src/cli/name-entries.ts +30 -18
- package/src/cli/native-smoke.ts +2 -0
- package/src/cli/not-a-ticker.ts +19 -9
- package/src/cli/opentui-libc.ts +5 -0
- package/src/cli/options.ts +12 -0
- package/src/cli/pane-functions/capabilities.ts +75 -20
- package/src/cli/pane-functions/catalog.ts +24 -23
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +10 -7
- package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
- package/src/cli/pane-functions/freshness.ts +174 -23
- package/src/cli/pane-functions/function-help.ts +66 -27
- package/src/cli/pane-functions/headless.ts +24 -11
- package/src/cli/pane-functions/index.ts +18 -16
- package/src/cli/pane-functions/options.ts +22 -2
- package/src/cli/pane-functions/report-notices.ts +12 -4
- package/src/cli/pane-functions/report.ts +5 -5
- package/src/cli/pane-functions/resolver.ts +47 -10
- package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
- package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
- package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
- package/src/cli/pane-functions/screenshot-payload.ts +453 -0
- package/src/cli/pane-functions/screenshot-render.ts +316 -0
- package/src/cli/pane-functions/screenshot-types.ts +162 -0
- package/src/cli/pane-functions/shot-options.ts +8 -3
- package/src/cli/peso-dollar-rates.ts +79 -0
- package/src/cli/quote-answer.ts +38 -3
- package/src/cli/report-tables.ts +61 -16
- package/src/cli/restore-plugins.ts +29 -14
- package/src/cli/result.ts +111 -22
- package/src/cli/series-search.ts +5 -5
- package/src/cli/statement-filings.ts +19 -0
- package/src/cli/suffixed-listing-rows.ts +49 -0
- package/src/cli/symbol-args.ts +50 -15
- package/src/cli/symbol-cells.ts +120 -0
- package/src/cli/symbol-list.ts +198 -68
- package/src/cli/symbol-rows.ts +131 -25
- package/src/cli/ticker-resolution.ts +108 -11
- package/src/cli/treasury-yields.ts +5 -5
- package/src/cli/types.ts +2 -0
- package/src/cli/uncovered-instrument.ts +3 -3
- package/src/cli/unknown-command.ts +6 -6
- package/src/components/chart/composite/chart-layout.ts +131 -0
- package/src/components/chart/composite/composite-chart.tsx +373 -16
- package/src/components/chart/composite/interactions.ts +7 -2
- package/src/components/chart/composite/legend.tsx +75 -3
- package/src/components/chart/composite/levels.ts +5 -0
- package/src/components/chart/composite/panel-gestures.ts +41 -0
- package/src/components/chart/composite/panel-labels.ts +2 -1
- package/src/components/chart/composite/panel-surface.tsx +305 -11
- package/src/components/chart/composite/price-axis-labels.tsx +27 -3
- package/src/components/chart/composite/rasterizer.ts +44 -19
- package/src/components/chart/composite/scene.ts +16 -4
- package/src/components/chart/composite/text-renderer.ts +48 -21
- package/src/components/chart/composite/toolbar.tsx +52 -5
- package/src/components/chart/composite/types.ts +13 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
- package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
- package/src/components/chart/composite/use-composite-viewport.ts +12 -3
- package/src/components/chart/composite/value-scale.ts +149 -0
- package/src/components/chart/composite/volume-profile-paint.ts +6 -3
- package/src/components/chart/core/pointer.ts +2 -0
- package/src/components/chart-table/index.ts +2 -0
- package/src/components/chart-table/read.tsx +74 -0
- package/src/components/command-bar/commands/collection-list.ts +41 -19
- package/src/components/command-bar/commands/collection.ts +9 -3
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +3 -2
- package/src/components/command-bar/pane-templates/items.ts +59 -6
- package/src/components/command-bar/pane-templates/workflow.ts +10 -1
- package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
- package/src/components/command-bar/routes/root/provider-search.ts +42 -11
- package/src/components/command-bar/routes/root/results.ts +18 -4
- package/src/components/command-bar/routes/root/runtime.ts +15 -3
- package/src/components/command-bar/routes/root/search-providers.ts +25 -2
- package/src/components/command-bar/routes/root/selection.ts +6 -1
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
- package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
- package/src/components/command-bar/selection-runtime.ts +50 -10
- package/src/components/command-bar/surface/index.tsx +24 -2
- package/src/components/command-bar/view-model.ts +7 -0
- package/src/components/command-bar/workflow/broker.ts +2 -2
- package/src/components/command-bar/workflow/collection-actions.ts +46 -7
- package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
- package/src/components/command-bar/workflow/fields.ts +8 -2
- package/src/components/command-bar/workflow/ops.ts +18 -4
- package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
- package/src/components/command-bar/workflow/submit.ts +3 -2
- package/src/components/command-bar/workflow/tickers.ts +5 -1
- package/src/components/company-logo.tsx +39 -0
- package/src/components/data-table/sections.ts +4 -2
- package/src/components/form-modal/confirm.tsx +2 -1
- package/src/components/form-modal/request.ts +5 -0
- package/src/components/form-modal/routes.ts +4 -1
- package/src/components/index.ts +12 -6
- package/src/components/layout/header.tsx +7 -6
- package/src/components/layout/market-summary.ts +9 -6
- package/src/components/layout/pane/capture-status.ts +1 -1
- package/src/components/layout/pane/footer/model.ts +11 -5
- package/src/components/layout/shell/chrome.ts +19 -6
- package/src/components/layout/shell/index.tsx +2 -1
- package/src/components/layout/shell/pane/layers.tsx +19 -1
- package/src/components/layout/status-bar.tsx +2 -2
- package/src/components/market-board/index.tsx +11 -3
- package/src/components/onboarding/onboarding-wizard.tsx +1 -0
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
- package/src/components/onboarding/wizard-broker-sync.ts +4 -1
- package/src/components/pane-settings-dialog/desktop.tsx +20 -7
- package/src/components/pane-settings-dialog/tui.tsx +17 -9
- package/src/components/pane-settings-dialog/value.ts +8 -1
- package/src/components/pane-settings-dialog.tsx +44 -13
- package/src/components/sign-in-code-panel.tsx +27 -19
- package/src/components/ui/column-grid.tsx +48 -4
- package/src/components/ui/country-flag-art.tsx +180 -0
- package/src/components/ui/country-flag.tsx +118 -12
- package/src/components/ui/data-table/opentui/index.tsx +61 -2
- package/src/components/ui/data-table/opentui/model.ts +109 -2
- package/src/components/ui/data-table/types.ts +1 -1
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/index.ts +3 -1
- package/src/components/ui/logo-mark.tsx +11 -2
- package/src/components/ui/query-bar.tsx +1 -1
- package/src/components/ui/row-notes.ts +79 -0
- package/src/core/state/app/layout.ts +4 -4
- package/src/data/cached-query.ts +8 -1
- package/src/data/config/chart-settings.ts +3 -3
- package/src/data/config/home.ts +10 -2
- package/src/data/config/layout.ts +4 -1
- package/src/data/config/store/node.ts +4 -0
- package/src/data/config/store/normalize.ts +13 -3
- package/src/data/config/store/portfolio-as-of-column.ts +2 -2
- package/src/data/json-storage.ts +2 -0
- package/src/data/saved-copies.ts +84 -0
- package/src/docs/snapshot.json +65 -32
- package/src/i18n/es.ts +29 -12
- package/src/i18n/ja.ts +29 -12
- package/src/i18n/ko.ts +29 -12
- package/src/i18n/zh-cn.ts +29 -12
- package/src/i18n/zh-tw.ts +29 -12
- package/src/layout/desks.ts +1 -1
- package/src/layout/launch-layout.ts +1 -1
- package/src/layout/pane-manager/floating-actions.ts +5 -3
- package/src/layout/pane-manager/floating.ts +43 -1
- package/src/layout/pane-manager/index.ts +2 -0
- package/src/layout/selected-ticker.ts +74 -0
- package/src/market-data/depositary-pair.ts +251 -0
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +124 -40
- package/src/market-data/fx-closes.ts +17 -2
- package/src/market-data/hooks.tsx +19 -11
- package/src/market-data/market/change-basis.ts +49 -7
- package/src/market-data/market/freshness.ts +34 -5
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/trading-sessions.ts +13 -0
- package/src/market-data/market/year-to-date.ts +8 -13
- package/src/market-data/performance.ts +20 -14
- package/src/market-data/perpetual-premium.ts +32 -0
- package/src/market-data/published-apac-sessions.ts +2 -1
- package/src/market-data/published-futures-sessions.ts +69 -0
- package/src/market-data/quotes/contributions.ts +5 -1
- package/src/market-data/quotes/freshness.ts +42 -0
- package/src/market-data/quotes/intraday-bar.ts +37 -0
- package/src/market-data/quotes/regular-session-valuation.ts +13 -8
- package/src/market-data/quotes/resolution.ts +4 -2
- package/src/market-data/tax-equivalent.ts +1 -1
- package/src/market-data/ticker-price-returns.ts +3 -1
- package/src/news/aggregator.ts +5 -1
- package/src/news/news-model.ts +4 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/app-notifier.ts +1 -1
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +9 -8
- package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
- package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
- package/src/plugins/builtin/alerts/cli.ts +485 -76
- package/src/plugins/builtin/alerts/command.ts +5 -10
- package/src/plugins/builtin/alerts/event-rows.ts +9 -5
- package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
- package/src/plugins/builtin/alerts/events.ts +107 -5
- package/src/plugins/builtin/alerts/fired-log.ts +78 -0
- package/src/plugins/builtin/alerts/format.ts +2 -1
- package/src/plugins/builtin/alerts/headless.ts +100 -39
- package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
- package/src/plugins/builtin/alerts/history-rows.ts +89 -0
- package/src/plugins/builtin/alerts/index.tsx +45 -18
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +3 -3
- package/src/plugins/builtin/alerts/pane.tsx +97 -29
- package/src/plugins/builtin/alerts/price.ts +35 -0
- package/src/plugins/builtin/alerts/quotes.ts +24 -0
- package/src/plugins/builtin/alerts/research-builder.ts +84 -11
- package/src/plugins/builtin/alerts/research-rules.ts +76 -3
- package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
- package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
- package/src/plugins/builtin/alerts/scheduled.ts +188 -29
- package/src/plugins/builtin/alerts/storage.ts +13 -10
- package/src/plugins/builtin/alerts/targets.ts +12 -3
- package/src/plugins/builtin/analytics/index.tsx +8 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -1
- package/src/plugins/builtin/analytics/risk-book.ts +215 -0
- package/src/plugins/builtin/analytics/risk-client.ts +13 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
- package/src/plugins/builtin/analytics/risk-math.ts +6 -3
- package/src/plugins/builtin/analytics/risk-model.ts +297 -104
- package/src/plugins/builtin/analytics/risk-options.ts +17 -2
- package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
- package/src/plugins/builtin/attention/pane.tsx +6 -2
- package/src/plugins/builtin/awards/headless.ts +7 -3
- package/src/plugins/builtin/awards/model.ts +18 -0
- package/src/plugins/builtin/awards/pane.tsx +6 -3
- package/src/plugins/builtin/backtest/headless.ts +3 -3
- package/src/plugins/builtin/backtest/rules.ts +41 -5
- package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
- package/src/plugins/builtin/broker-manager/footer.ts +27 -11
- package/src/plugins/builtin/broker-manager/index.tsx +21 -12
- package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
- package/src/plugins/builtin/broker-manager/model.ts +12 -2
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
- package/src/plugins/builtin/broker-manager/table.ts +28 -12
- package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
- package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
- package/src/plugins/builtin/broker-trading/controller.ts +45 -13
- package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
- package/src/plugins/builtin/broker-trading/headless.ts +2 -1
- package/src/plugins/builtin/broker-trading/index.tsx +7 -1
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
- package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
- package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
- package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
- package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
- package/src/plugins/builtin/buildout/cells.tsx +4 -0
- package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
- package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
- package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
- package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
- package/src/plugins/builtin/catalysts/detail.tsx +3 -2
- package/src/plugins/builtin/catalysts/index.tsx +1 -1
- package/src/plugins/builtin/catalysts/model.ts +49 -4
- package/src/plugins/builtin/catalysts/pane.tsx +6 -8
- package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
- package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
- package/src/plugins/builtin/changelog/index.tsx +1 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
- package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
- package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
- package/src/plugins/builtin/chart-composer/headless.ts +253 -35
- package/src/plugins/builtin/chart-composer/index.tsx +12 -2
- package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
- package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
- package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
- package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
- package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
- package/src/plugins/builtin/chart-composer/settings.ts +31 -19
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
- package/src/plugins/builtin/chat/attachments/model.ts +3 -1
- package/src/plugins/builtin/chat/content/index.tsx +1 -1
- package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
- package/src/plugins/builtin/chat/people.ts +1 -1
- package/src/plugins/builtin/clinical-trials/client.ts +5 -2
- package/src/plugins/builtin/clinical-trials/model.ts +13 -4
- package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
- package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
- package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
- package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
- package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
- package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
- package/src/plugins/builtin/company-attention/building-model.ts +222 -0
- package/src/plugins/builtin/company-attention/building.tsx +172 -0
- package/src/plugins/builtin/company-attention/client.ts +117 -1
- package/src/plugins/builtin/company-attention/evidence.ts +16 -3
- package/src/plugins/builtin/company-attention/headless.ts +101 -5
- package/src/plugins/builtin/company-attention/index.tsx +6 -3
- package/src/plugins/builtin/company-attention/model.ts +33 -7
- package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
- package/src/plugins/builtin/company-attention/moves.tsx +159 -0
- package/src/plugins/builtin/company-attention/pane.tsx +79 -23
- package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
- package/src/plugins/builtin/company-attention/signals.tsx +160 -0
- package/src/plugins/builtin/company-kpis/headless.ts +3 -2
- package/src/plugins/builtin/company-kpis/model.ts +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/congress-trades/headless.ts +1 -1
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
- package/src/plugins/builtin/correlation/headless.ts +5 -3
- package/src/plugins/builtin/cot/headless.ts +28 -6
- package/src/plugins/builtin/cot/model.ts +12 -0
- package/src/plugins/builtin/cot/pane.tsx +3 -2
- package/src/plugins/builtin/cpi/headless.ts +5 -1
- package/src/plugins/builtin/cpi/pane.tsx +11 -9
- package/src/plugins/builtin/credit-boards/model.ts +2 -0
- package/src/plugins/builtin/credit-documents/headless.ts +1 -1
- package/src/plugins/builtin/crypto-board/client.ts +86 -0
- package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
- package/src/plugins/builtin/crypto-board/defi.ts +239 -0
- package/src/plugins/builtin/crypto-board/headless.ts +117 -6
- package/src/plugins/builtin/crypto-board/index.tsx +8 -3
- package/src/plugins/builtin/crypto-board/model.ts +40 -8
- package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
- package/src/plugins/builtin/crypto-board/series.ts +19 -1
- package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
- package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
- package/src/plugins/builtin/debug/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/docs/index.tsx +1 -1
- package/src/plugins/builtin/docs/pane.tsx +2 -2
- package/src/plugins/builtin/docs/targets.ts +1 -1
- package/src/plugins/builtin/doe/client.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +12 -6
- package/src/plugins/builtin/doe/model.ts +43 -6
- package/src/plugins/builtin/doe/pane.tsx +18 -11
- package/src/plugins/builtin/earnings/board-model.ts +13 -6
- package/src/plugins/builtin/earnings/board-table.ts +4 -1
- package/src/plugins/builtin/earnings/board.tsx +253 -50
- package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
- package/src/plugins/builtin/earnings/calendar.tsx +338 -0
- package/src/plugins/builtin/earnings/client.ts +4 -3
- package/src/plugins/builtin/earnings/headless.ts +53 -15
- package/src/plugins/builtin/earnings/history-model.ts +53 -36
- package/src/plugins/builtin/earnings/index.tsx +15 -22
- package/src/plugins/builtin/earnings/model.ts +17 -12
- package/src/plugins/builtin/earnings/scope.ts +89 -0
- package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
- package/src/plugins/builtin/earnings-calls/data.ts +146 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
- package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
- package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
- package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +132 -20
- package/src/plugins/builtin/econ/detail-view.tsx +48 -10
- package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
- package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
- package/src/plugins/builtin/econ/index.tsx +112 -25
- package/src/plugins/builtin/econ/print-watch.ts +2 -1
- package/src/plugins/builtin/econ/types.ts +32 -1
- package/src/plugins/builtin/econ-statistics/client.ts +16 -3
- package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
- package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
- package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
- package/src/plugins/builtin/econ-statistics/view.ts +16 -1
- package/src/plugins/builtin/equity-screener/client.ts +21 -3
- package/src/plugins/builtin/equity-screener/headless.ts +68 -6
- package/src/plugins/builtin/equity-screener/live.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +6 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
- package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
- package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
- package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
- package/src/plugins/builtin/exposure/headless.ts +51 -6
- package/src/plugins/builtin/exposure/index.tsx +28 -2
- package/src/plugins/builtin/exposure/model.ts +7 -0
- package/src/plugins/builtin/exposure/pane.tsx +6 -1
- package/src/plugins/builtin/fear-greed/index.tsx +1 -1
- package/src/plugins/builtin/filing-events/index.tsx +1 -1
- package/src/plugins/builtin/futures/model.ts +10 -9
- package/src/plugins/builtin/futures/returns.ts +8 -36
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +40 -9
- package/src/plugins/builtin/futures-curve/headless.ts +39 -17
- package/src/plugins/builtin/futures-curve/model.ts +258 -69
- package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
- package/src/plugins/builtin/fx-matrix/client.ts +15 -8
- package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
- package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
- package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
- package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
- package/src/plugins/builtin/gpu/headless.ts +9 -4
- package/src/plugins/builtin/help/index.tsx +13 -3
- package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
- package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +13 -1
- package/src/plugins/builtin/holders/format.ts +28 -17
- package/src/plugins/builtin/holders/headless.ts +46 -7
- package/src/plugins/builtin/holders/index-holders.ts +264 -0
- package/src/plugins/builtin/holders/pane.tsx +79 -15
- package/src/plugins/builtin/holders/report-rows.ts +35 -7
- package/src/plugins/builtin/holders/table-model.ts +2 -1
- package/src/plugins/builtin/holders/treemap.tsx +2 -2
- package/src/plugins/builtin/insider/headless.ts +1 -1
- package/src/plugins/builtin/insider/signals-client.ts +1 -1
- package/src/plugins/builtin/insider/signals.tsx +1 -1
- package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
- package/src/plugins/builtin/iv-history/client.ts +20 -0
- package/src/plugins/builtin/iv-history/headless.ts +4 -4
- package/src/plugins/builtin/iv-history/index.tsx +1 -1
- package/src/plugins/builtin/iv-history/model.ts +13 -4
- package/src/plugins/builtin/iv-history/series.ts +3 -1
- package/src/plugins/builtin/jobs/lock-note.ts +1 -1
- package/src/plugins/builtin/jobs/model.ts +4 -15
- package/src/plugins/builtin/jobs/pane.tsx +7 -5
- package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
- package/src/plugins/builtin/jodi/pane.tsx +10 -4
- package/src/plugins/builtin/layout-manager/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +20 -7
- package/src/plugins/builtin/macro-day/index.tsx +3 -2
- package/src/plugins/builtin/macro-day/model.ts +29 -8
- package/src/plugins/builtin/macro-day/pane.tsx +46 -10
- package/src/plugins/builtin/macro-day/releases.ts +55 -13
- package/src/plugins/builtin/market-heatmap/data.ts +9 -2
- package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
- package/src/plugins/builtin/market-heatmap/model.ts +10 -7
- package/src/plugins/builtin/market-movers/client.ts +33 -19
- package/src/plugins/builtin/market-movers/headless.ts +8 -2
- package/src/plugins/builtin/market-movers/index.tsx +16 -10
- package/src/plugins/builtin/market-movers/screener.ts +29 -8
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
- package/src/plugins/builtin/market-movers/session-table.ts +4 -2
- package/src/plugins/builtin/market-movers/table.ts +4 -1
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/market-valuation/view.ts +5 -0
- package/src/plugins/builtin/members/headless.ts +6 -5
- package/src/plugins/builtin/members/pane.tsx +11 -4
- package/src/plugins/builtin/mna/client.ts +1 -0
- package/src/plugins/builtin/mna/detail.tsx +20 -32
- package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
- package/src/plugins/builtin/mna/distress/model.ts +3 -2
- package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
- package/src/plugins/builtin/mna/headless.ts +6 -13
- package/src/plugins/builtin/mna/model.ts +93 -15
- package/src/plugins/builtin/mna/pane.tsx +5 -4
- package/src/plugins/builtin/news/headless.ts +12 -6
- package/src/plugins/builtin/news/index.tsx +10 -1
- package/src/plugins/builtin/news/wire/index.tsx +3 -2
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
- package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
- package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
- package/src/plugins/builtin/notes/cli.ts +49 -14
- package/src/plugins/builtin/openfda/pane.tsx +1 -1
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/view.tsx +10 -10
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-scenario/client.ts +123 -28
- package/src/plugins/builtin/options-scenario/collar.ts +8 -6
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
- package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
- package/src/plugins/builtin/options-scenario/csp.ts +101 -21
- package/src/plugins/builtin/options-scenario/headless.ts +36 -15
- package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
- package/src/plugins/builtin/options-scenario/index.tsx +28 -3
- package/src/plugins/builtin/options-scenario/model.ts +13 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
- package/src/plugins/builtin/perps/equity-row.tsx +11 -3
- package/src/plugins/builtin/perps/freshness.ts +8 -5
- package/src/plugins/builtin/perps/headless.ts +3 -1
- package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
- package/src/plugins/builtin/perps/live-rows.ts +161 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/pane.tsx +36 -17
- package/src/plugins/builtin/perps/venue-markets.ts +96 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
- package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
- package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
- package/src/plugins/builtin/portfolio-list/income.ts +13 -2
- package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
- package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
- package/src/plugins/builtin/power/client.ts +4 -1
- package/src/plugins/builtin/power/evidence.ts +22 -7
- package/src/plugins/builtin/power/gas.tsx +5 -1
- package/src/plugins/builtin/power/pane.tsx +8 -1
- package/src/plugins/builtin/power/prices.tsx +10 -3
- package/src/plugins/builtin/research/analyst-headless.ts +25 -8
- package/src/plugins/builtin/research/analyst-model.ts +68 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
- package/src/plugins/builtin/research/event-model.ts +3 -1
- package/src/plugins/builtin/research/event-sources.ts +12 -4
- package/src/plugins/builtin/research/index.tsx +2 -1
- package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
- package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
- package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
- package/src/plugins/builtin/risk-factors/index.tsx +2 -2
- package/src/plugins/builtin/scanner/lock-note.ts +1 -1
- package/src/plugins/builtin/sec/forms.ts +13 -0
- package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
- package/src/plugins/builtin/sec/fund-facts.ts +68 -8
- package/src/plugins/builtin/sec/headless.ts +85 -8
- package/src/plugins/builtin/sec/index.tsx +58 -12
- package/src/plugins/builtin/sectors/client.ts +2 -1
- package/src/plugins/builtin/sectors/sector-model.ts +11 -15
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
- package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
- package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
- package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
- package/src/plugins/builtin/shared/earnings-report.ts +3 -3
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
- package/src/plugins/builtin/shared/job-posting.ts +129 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
- package/src/plugins/builtin/shared/report-freshness.ts +3 -0
- package/src/plugins/builtin/shared/research-trading.tsx +21 -0
- package/src/plugins/builtin/shared/series-cache.ts +2 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
- package/src/plugins/builtin/short-interest/client.ts +2 -0
- package/src/plugins/builtin/short-interest/headless.ts +41 -12
- package/src/plugins/builtin/short-interest/index.tsx +23 -1
- package/src/plugins/builtin/short-interest/model.ts +70 -19
- package/src/plugins/builtin/short-interest/pane.tsx +19 -2
- package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
- package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
- package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
- package/src/plugins/builtin/short-interest/schedule.ts +35 -2
- package/src/plugins/builtin/short-interest/surface.tsx +11 -4
- package/src/plugins/builtin/short-interest/types.ts +9 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
- package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
- package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
- package/src/plugins/builtin/supply-chain/model.ts +51 -3
- package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
- package/src/plugins/builtin/themes/headless.ts +8 -5
- package/src/plugins/builtin/themes/model.ts +14 -4
- package/src/plugins/builtin/themes/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/headless.ts +5 -5
- package/src/plugins/builtin/thirteenf/signals.ts +3 -1
- package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
- package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
- package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
- package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
- package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
- package/src/plugins/builtin/vol-surface/client.ts +5 -4
- package/src/plugins/builtin/vol-surface/headless.ts +12 -3
- package/src/plugins/builtin/vol-surface/model.ts +4 -2
- package/src/plugins/builtin/volatility/model.ts +5 -10
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
- package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
- package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
- package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +3 -26
- package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
- package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
- package/src/plugins/builtin/yield-curve/client.ts +25 -0
- package/src/plugins/builtin/yield-curve/curves.ts +7 -4
- package/src/plugins/builtin/yield-curve/headless.ts +38 -4
- package/src/plugins/builtin/yield-curve/history.ts +4 -3
- package/src/plugins/builtin/yield-curve/index.tsx +12 -6
- package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
- package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
- package/src/plugins/bundle.ts +1 -1
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/compat.ts +3 -3
- package/src/plugins/dependencies.ts +75 -14
- package/src/plugins/extracted-plugins.ts +132 -34
- package/src/plugins/host-link.ts +149 -34
- package/src/plugins/installer.ts +35 -10
- package/src/plugins/loader.ts +206 -38
- package/src/plugins/manager-node.ts +2 -2
- package/src/plugins/marketplace/deactivation.ts +22 -0
- package/src/plugins/marketplace/model.ts +2 -2
- package/src/plugins/marketplace/packs.ts +1 -1
- package/src/plugins/ownership.ts +1 -1
- package/src/plugins/peer-retry.ts +155 -0
- package/src/plugins/registry/contributions.ts +3 -1
- package/src/plugins/registry/index.ts +4 -3
- package/src/plugins/registry/plugin-state.ts +3 -0
- package/src/plugins/seed-notice.ts +20 -0
- package/src/plugins/shortcut-argument.ts +39 -0
- package/src/portfolio/allocation.ts +1 -1
- package/src/portfolio/collection-name.ts +31 -0
- package/src/portfolio/column-values.ts +10 -8
- package/src/portfolio/cost-fx.ts +296 -19
- package/src/portfolio/manual-lots.ts +75 -0
- package/src/portfolio/mutations.ts +60 -22
- package/src/portfolio/suffixed-listings.ts +209 -0
- package/src/portfolio/totals-currency.ts +24 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
- package/src/public/time-series.ts +11 -8
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/config-host.ts +3 -0
- package/src/renderers/browser/main.tsx +23 -3
- package/src/renderers/dom/boot-frame-page.ts +187 -0
- package/src/renderers/dom/boot-frame.ts +237 -0
- package/src/renderers/dom/build-assets.ts +46 -8
- package/src/renderers/dom/dom-ui-host.tsx +4 -29
- package/src/renderers/dom/host/ascii-text.ts +16 -2
- package/src/renderers/dom/host/box.tsx +43 -2
- package/src/renderers/dom/host/chart-surface.tsx +2 -0
- package/src/renderers/dom/host/hls.chunk.ts +3 -0
- package/src/renderers/dom/host/image-surface.tsx +49 -0
- package/src/renderers/dom/host/media-surface.tsx +24 -18
- package/src/renderers/dom/host/mouse.ts +30 -0
- package/src/renderers/dom/host/size-observer.ts +1 -1
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/styles.css +76 -0
- package/src/renderers/dom/test-utils.tsx +2 -1
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
- package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
- package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
- package/src/renderers/electrobun/bun/index.ts +11 -1
- package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -1
- package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
- package/src/renderers/electrobun/shared/protocol.ts +6 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/config-host.ts +3 -0
- package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
- package/src/renderers/electrobun/view/main.tsx +30 -4
- package/src/renderers/electrobun/view/news-source.ts +5 -5
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -1
- package/src/renderers/opentui/browser-open.ts +88 -0
- package/src/renderers/opentui/host.tsx +5 -14
- package/src/renderers/opentui/image/loader.ts +3 -2
- package/src/renderers/opentui/native-smoke.ts +10 -1
- package/src/renderers/opentui/pane-image/index.ts +2 -1
- package/src/renderers/opentui/render-lib.ts +253 -0
- package/src/renderers/opentui/render-timing-smoke.ts +1 -1
- package/src/renderers/opentui/start.tsx +1 -1
- package/src/renderers/share/layout-view.tsx +2 -2
- package/src/renderers/share/main.tsx +4 -4
- package/src/renderers/share/view.tsx +1 -1
- package/src/series/resolve.ts +22 -6
- package/src/series/standalone.ts +30 -2
- package/src/series/types.ts +8 -1
- package/src/shares/api.ts +4 -1
- package/src/shares/pane.ts +1 -1
- package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
- package/src/sources/gloomberb-cloud/index.ts +32 -16
- package/src/sources/gloomberb-cloud/news.ts +39 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
- package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
- package/src/sources/listing-symbols.ts +37 -0
- package/src/sources/provider-errors.ts +16 -2
- package/src/sources/provider-router/financial-routes.ts +21 -3
- package/src/sources/provider-router/financials.ts +15 -2
- package/src/sources/provider-router/history.ts +30 -8
- package/src/sources/provider-router/primary.ts +1 -1
- package/src/state/app/bootstrap.ts +23 -0
- package/src/state/hooks/inline-tickers.ts +30 -5
- package/src/sync/controller.ts +1 -1
- package/src/sync/core-contributors.ts +46 -14
- package/src/team/collections-sync.ts +2 -1
- package/src/team/collections.ts +178 -17
- package/src/telemetry/crash-reports.ts +17 -4
- package/src/test-support/builtin-panes.ts +1 -1
- package/src/theme/colors.ts +3 -30
- package/src/theme/ui-tokens.ts +37 -1
- package/src/tickers/company-name-listing.ts +210 -33
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- package/src/tickers/metadata.ts +10 -0
- package/src/tickers/perpetual-listing.ts +5 -2
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/index.ts +76 -27
- package/src/tickers/search/ranking.ts +54 -4
- package/src/tickers/search/upsert.ts +29 -0
- package/src/tickers/terminal-codes.ts +416 -40
- package/src/time-series/bar-aggregation.ts +243 -0
- package/src/time-series/base-series.ts +307 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/economic.ts +5 -0
- package/src/time-series/fundamentals.ts +18 -3
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/leverage.ts +100 -0
- package/src/time-series/market-resolution.ts +4 -2
- package/src/time-series/market.ts +2 -1
- package/src/time-series/price-comparison.ts +175 -30
- package/src/time-series/range.ts +9 -1
- package/src/time-series/resolution.ts +174 -16
- package/src/time-series/resolve-context.ts +157 -0
- package/src/time-series/resolve.ts +126 -1577
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +208 -0
- package/src/time-series/spec.ts +11 -6
- package/src/time-series/studies.ts +109 -12
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/types.ts +29 -2
- package/src/time-series/use-chart-resolution.ts +4 -5
- package/src/types/broker.ts +10 -0
- package/src/types/config.ts +14 -0
- package/src/types/data-provider.ts +14 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +104 -7
- package/src/types/headless.ts +76 -6
- package/src/types/instrument.ts +3 -0
- package/src/types/plugin.ts +65 -8
- package/src/types/price-history.ts +2 -0
- package/src/types/ticker.ts +13 -0
- package/src/types/trading.ts +59 -1
- package/src/ui/ascii-font.ts +13 -1
- package/src/ui/host.tsx +6 -1
- package/src/updater/command-names.ts +175 -0
- package/src/updater/index.ts +10 -5
- package/src/utils/calendar-date.ts +32 -15
- package/src/utils/cash-runway.ts +241 -0
- package/src/utils/central-banks.ts +42 -0
- package/src/utils/cli-output.ts +259 -80
- package/src/utils/contract-month.ts +16 -0
- package/src/utils/crypto-pair.ts +61 -3
- package/src/utils/currency-units.ts +29 -0
- package/src/utils/datetime-format.ts +10 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/decimal-comma.ts +39 -6
- package/src/utils/exchange-announcements.ts +71 -0
- package/src/utils/exchange-rate-snapshot.ts +10 -0
- package/src/utils/financial-statements.ts +83 -4
- package/src/utils/format.ts +34 -16
- package/src/utils/fundamentals.ts +150 -34
- package/src/utils/fx-market-hours.ts +10 -1
- package/src/utils/latest-financial-period.ts +8 -0
- package/src/utils/linux-libc.ts +54 -0
- package/src/utils/market-capitalization.ts +111 -15
- package/src/utils/max-drawdown.ts +64 -0
- package/src/utils/network-failure.ts +83 -0
- package/src/utils/option-expiry.ts +139 -7
- package/src/utils/price-earnings.ts +11 -5
- package/src/utils/price-history.ts +18 -8
- package/src/utils/price-window.ts +131 -0
- package/src/utils/release-date.ts +47 -0
- package/src/utils/sec.ts +3 -0
- package/src/utils/throttled-fetch.ts +11 -4
- package/src/utils/trailing-eps.ts +67 -0
- package/src/utils/typed-text.ts +21 -0
- package/src/utils/utc-time.ts +24 -1
- package/src/version.ts +6 -0
- package/src/cli/pane-functions/screenshot.ts +0 -2122
|
@@ -46,17 +46,20 @@ import { useResearchCloudSession } from "../shared/research-cloud-session";
|
|
|
46
46
|
import { loadPortfolioRiskMarket } from "./risk-client";
|
|
47
47
|
import { brokerPerformanceEvidence, parsePortfolioRiskEvidence } from "./risk-evidence";
|
|
48
48
|
import { useBrokerPortfolioPerformance } from "./broker-performance";
|
|
49
|
+
import { parseAssetShifts, parseFxShifts, riskCashLines } from "./risk-book";
|
|
50
|
+
import { resolvePortfolioAccountState } from "../portfolio-list/summary";
|
|
49
51
|
import {
|
|
50
52
|
BASKET_VIEWS,
|
|
51
53
|
buildPortfolioRisk,
|
|
54
|
+
HOLDINGS_SHARE_UNIT,
|
|
52
55
|
HOLDINGS_SUMMARY_ROW_IDS,
|
|
56
|
+
isShockRow,
|
|
53
57
|
portfolioRiskRequests,
|
|
54
58
|
portfolioRiskTickers,
|
|
55
|
-
|
|
59
|
+
riskCoverageLine,
|
|
56
60
|
riskCoverageNotices,
|
|
57
61
|
riskCoverageShortfall,
|
|
58
62
|
riskPercentile,
|
|
59
|
-
riskAmountBase,
|
|
60
63
|
riskEvidence,
|
|
61
64
|
riskReturnsCurrency,
|
|
62
65
|
riskValue,
|
|
@@ -98,6 +101,10 @@ const LABEL_HEADERS: Record<RiskView, string> = {
|
|
|
98
101
|
greeks: "Metric",
|
|
99
102
|
};
|
|
100
103
|
const EVIDENCE_VIEWS: ReadonlySet<RiskView> = new Set(["performance", "attribution", "greeks"]);
|
|
104
|
+
/** The risk view's tail rows, whose method the footer names. */
|
|
105
|
+
const TAIL_ROW_IDS: ReadonlySet<string> = new Set(["var", "es"]);
|
|
106
|
+
/** The views that read the basket's daily closes, and say to when; Holdings reads current marks. */
|
|
107
|
+
const DAILY_CLOSE_VIEWS: ReadonlySet<RiskView> = new Set(["risk", "factors", "correlation", "stress"]);
|
|
101
108
|
const ATTRIBUTION_PARTS = [
|
|
102
109
|
{ id: "allocation", label: "Allocation" },
|
|
103
110
|
{ id: "selection", label: "Selection" },
|
|
@@ -113,13 +120,17 @@ const bareValue = (row: RiskDisplayRow) => row.value == null ? "--" : row.value.
|
|
|
113
120
|
const HOLDINGS_SUMMARY_LABELS: Record<string, string> = { top: "Largest", "top-five": "Top five" };
|
|
114
121
|
/** The book concentration figures that lead the holdings view, as a summary band. */
|
|
115
122
|
function holdingsSummaryItems(rows: RiskDisplayRow[]): StatItem[] {
|
|
116
|
-
return rows.map((row) =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
return rows.map((row) => {
|
|
124
|
+
// A snapshot saved before the shares named their base reads "% value".
|
|
125
|
+
const share = row.unit === HOLDINGS_SHARE_UNIT || row.unit === "% value";
|
|
126
|
+
return {
|
|
127
|
+
id: row.id,
|
|
128
|
+
label: HOLDINGS_SUMMARY_LABELS[row.id] ?? row.label,
|
|
129
|
+
value: row.value == null ? "--" : share ? `${row.value.toFixed(2)}%` : row.value.toFixed(2),
|
|
130
|
+
// The same note sits on both shares: what they divide.
|
|
131
|
+
detail: share ? (row.unit === HOLDINGS_SHARE_UNIT ? "of holdings, cash aside" : "of market value") : row.detail,
|
|
132
|
+
};
|
|
133
|
+
});
|
|
123
134
|
}
|
|
124
135
|
/**
|
|
125
136
|
* Cumulative return of the basket and of its benchmark across the window of
|
|
@@ -224,7 +235,8 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
224
235
|
const instance = usePaneInstance(),
|
|
225
236
|
renderer = useRendererHost();
|
|
226
237
|
const portfolios = useAppSelector((state) => state.config.portfolios),
|
|
227
|
-
tickers = useAppSelector((state) => state.tickers)
|
|
238
|
+
tickers = useAppSelector((state) => state.tickers),
|
|
239
|
+
brokerAccounts = useAppSelector((state) => state.brokerAccounts);
|
|
228
240
|
const focusedCollection = useAppSelector((state) =>
|
|
229
241
|
getFocusedCollectionId(state),
|
|
230
242
|
);
|
|
@@ -271,8 +283,33 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
271
283
|
const [equity] = usePaneSettingValue("equityShift", -10),
|
|
272
284
|
[rates] = usePaneSettingValue("rateShift", 100),
|
|
273
285
|
[volatility] = usePaneSettingValue("volShift", 10),
|
|
286
|
+
[fxShift] = usePaneSettingValue("fxShift", ""),
|
|
287
|
+
[assetShift] = usePaneSettingValue("assetShift", ""),
|
|
274
288
|
[since] = usePaneSettingValue("riskSince", "trailing"),
|
|
275
289
|
[weights] = usePaneSettingValue("riskWeights", "13f");
|
|
290
|
+
// Currency and price shocks as the settings give them; one that does not read is named in the footer, not applied.
|
|
291
|
+
const shocks = useMemo(() => {
|
|
292
|
+
const errors: string[] = [];
|
|
293
|
+
function read<T>(text: unknown, parse: (text: string, flag: string) => T[], label: string): T[] {
|
|
294
|
+
if (!String(text ?? "").trim()) return [];
|
|
295
|
+
try {
|
|
296
|
+
return parse(String(text), label);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
errors.push(error instanceof Error ? error.message : `${label} unreadable`);
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return { fx: read(fxShift, parseFxShifts, "FX shift"), assets: read(assetShift, parseAssetShifts, "Asset shift"), errors };
|
|
303
|
+
}, [fxShift, assetShift]);
|
|
304
|
+
// The cash beside the holdings: the broker account's last saved balances by currency, else the portfolio's own.
|
|
305
|
+
const cash = useMemo(() => {
|
|
306
|
+
if (!portfolio) return [];
|
|
307
|
+
const account = resolvePortfolioAccountState(portfolio, { config, brokerAccounts }, { status: null, accounts: [] })?.account;
|
|
308
|
+
return riskCashLines(portfolio, account);
|
|
309
|
+
}, [brokerAccounts, config, portfolio]);
|
|
310
|
+
// The currencies the load prices, kept by value so a resynced account with the same ones reloads nothing.
|
|
311
|
+
const cashKey = cash.map((line) => line.currency).join(",");
|
|
312
|
+
const cashCurrencies = useMemo(() => (cashKey ? cashKey.split(",") : []), [cashKey]);
|
|
276
313
|
const riskWindow = useMemo(
|
|
277
314
|
() => (since === "filed" ? { since: "filed" as const, weights: weights === "current" ? "current" as const : "13f" as const } : {}),
|
|
278
315
|
[since, weights],
|
|
@@ -302,12 +339,12 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
302
339
|
const loader = useCallback(
|
|
303
340
|
async (force: boolean) => {
|
|
304
341
|
const [market, brokerOptions] = await Promise.all([
|
|
305
|
-
loadPortfolioRiskMarket(requests, force, portfolio!.currency),
|
|
342
|
+
loadPortfolioRiskMarket(requests, force, portfolio!.currency, cashCurrencies),
|
|
306
343
|
loadPortfolioOptionBook(localTickers, portfolio!),
|
|
307
344
|
]);
|
|
308
345
|
return { ...market, brokerOptions };
|
|
309
346
|
},
|
|
310
|
-
[requests, localTickers, portfolio, session.requestKey],
|
|
347
|
+
[requests, localTickers, portfolio, cashCurrencies, session.requestKey],
|
|
311
348
|
);
|
|
312
349
|
const resource = useAsyncResource(!frozen && portfolio ? loader : null, {
|
|
313
350
|
clearOnError: isAccessDenied,
|
|
@@ -335,14 +372,17 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
335
372
|
equity: Number(equity),
|
|
336
373
|
rates: Number(rates),
|
|
337
374
|
volatility: Number(volatility),
|
|
375
|
+
fx: shocks.fx,
|
|
376
|
+
assets: shocks.assets,
|
|
338
377
|
},
|
|
339
378
|
riskWindow,
|
|
379
|
+
cash,
|
|
340
380
|
),
|
|
341
381
|
error: null,
|
|
342
382
|
};
|
|
343
383
|
} catch (error) {
|
|
344
384
|
return {
|
|
345
|
-
model: buildPortfolioRisk(portfolio, localTickers, market, null, undefined, riskWindow),
|
|
385
|
+
model: buildPortfolioRisk(portfolio, localTickers, market, null, undefined, riskWindow, cash),
|
|
346
386
|
error: error instanceof Error ? error.message : "Evidence invalid",
|
|
347
387
|
};
|
|
348
388
|
}
|
|
@@ -356,7 +396,9 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
356
396
|
equity,
|
|
357
397
|
rates,
|
|
358
398
|
volatility,
|
|
399
|
+
shocks,
|
|
359
400
|
riskWindow,
|
|
401
|
+
cash,
|
|
360
402
|
]);
|
|
361
403
|
const model = derived.model;
|
|
362
404
|
const marketLoading = resource.loading && !resource.data;
|
|
@@ -365,8 +407,16 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
365
407
|
if (!model || !BASKET_VIEWS.has(view)) return null;
|
|
366
408
|
if (!frozen && !resource.data)
|
|
367
409
|
return marketLoading ? { loading: true } : resource.error ? { error: resource.error } : null;
|
|
368
|
-
|
|
410
|
+
if (view === "holdings") return null;
|
|
411
|
+
const shortfall = riskCoverageShortfall(model.coverage);
|
|
412
|
+
// The currency and price shocks reprice the book without the basket, so they stay; the footer says why the basket scenarios do not.
|
|
413
|
+
return shortfall && view === "stress" && model.rows.stress.some(isShockRow) ? null : shortfall;
|
|
369
414
|
}, [frozen, marketLoading, model, resource.data, resource.error, view]);
|
|
415
|
+
// Stress with too little covered for the basket scenarios, its shocks shown alone.
|
|
416
|
+
const stressShortfall = useMemo(
|
|
417
|
+
() => (!basketBody && model && view === "stress" ? riskCoverageShortfall(model.coverage) : null),
|
|
418
|
+
[basketBody, model, view],
|
|
419
|
+
);
|
|
370
420
|
const holdingsSummary = useMemo(
|
|
371
421
|
() => view === "holdings" ? holdingsSummaryItems((model?.rows.holdings ?? []).filter((row) => HOLDINGS_SUMMARY_ROW_IDS.has(row.id))) : [],
|
|
372
422
|
[model, view],
|
|
@@ -375,6 +425,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
375
425
|
() =>
|
|
376
426
|
(basketBody ? [] : model?.rows[view] ?? [])
|
|
377
427
|
.filter((row) => view !== "holdings" || !HOLDINGS_SUMMARY_ROW_IDS.has(row.id))
|
|
428
|
+
.filter((row) => !stressShortfall || isShockRow(row))
|
|
378
429
|
.sort((a, b) => {
|
|
379
430
|
if (!sort.column) return 0;
|
|
380
431
|
const left = a[sort.column as keyof RiskDisplayRow],
|
|
@@ -388,7 +439,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
388
439
|
(sort.direction === "asc" ? 1 : -1)
|
|
389
440
|
);
|
|
390
441
|
}),
|
|
391
|
-
[basketBody, model, view, sort],
|
|
442
|
+
[basketBody, stressShortfall, model, view, sort],
|
|
392
443
|
);
|
|
393
444
|
const openRow = rows.find((row) => row.id === open);
|
|
394
445
|
const detailScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
@@ -401,6 +452,8 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
401
452
|
rows.length > 1 && rows.every((row) => row.detail === rows[0]!.detail)
|
|
402
453
|
? rows[0]!.detail
|
|
403
454
|
: null;
|
|
455
|
+
// The risk rows read their window in their labels, so the footer names how the tail rows are estimated instead of a column.
|
|
456
|
+
const footerEvidence = (view === "risk" ? rows.find((row) => TAIL_ROW_IDS.has(row.id))?.detail : null) ?? sharedEvidence;
|
|
404
457
|
const importEvidence = useCallback(async () => {
|
|
405
458
|
try {
|
|
406
459
|
const text = await renderer.readText(),
|
|
@@ -461,30 +514,37 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
461
514
|
...riskCoverageNotices(model?.coverage),
|
|
462
515
|
...(model?.notes ?? []),
|
|
463
516
|
...(model?.warnings ?? []),
|
|
517
|
+
...shocks.errors,
|
|
464
518
|
...(derived.error ? [derived.error] : []),
|
|
465
519
|
...(resource.error ? [resource.error] : []),
|
|
466
520
|
];
|
|
467
|
-
// What the basket views leave out, unless the body already says why they show nothing.
|
|
468
|
-
|
|
469
|
-
const
|
|
521
|
+
// What the basket views cover of the book and leave out, unless the body already says why they show nothing.
|
|
522
|
+
// Stress showing its shocks alone says instead why the basket scenarios are not there.
|
|
523
|
+
const coverageText = !model || !BASKET_VIEWS.has(view) || basketBody
|
|
524
|
+
? null
|
|
525
|
+
: stressShortfall
|
|
526
|
+
? `Index, 10Y and VIX scenarios: ${stressShortfall.title.replace(/\.$/, "").replace(/^[A-Z](?=[a-z])/, (letter) => letter.toLowerCase())}`
|
|
527
|
+
: riskCoverageLine(model.coverage);
|
|
528
|
+
const asOfDate = (stressShortfall ? rows : model?.rows[view])?.find((row) => row.asOf)?.asOf?.slice(0, 10) ?? null;
|
|
470
529
|
// The views estimated from returns name the currency a non-USD account's returns were converted into.
|
|
471
|
-
const returnsCurrency = BASKET_VIEWS.has(view) && view !== "holdings" ? riskReturnsCurrency(model) : null;
|
|
472
|
-
const amountBase = rows.map(riskAmountBase).find(Boolean);
|
|
530
|
+
const returnsCurrency = BASKET_VIEWS.has(view) && view !== "holdings" && !stressShortfall ? riskReturnsCurrency(model) : null;
|
|
473
531
|
// Dated evidence only when a row has a date; a missing date is not a claim about history.
|
|
474
532
|
const asOfText = !model
|
|
475
533
|
? null
|
|
476
534
|
: [
|
|
477
535
|
...(basketBody ? [] : [
|
|
478
|
-
...(
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
: [returnsCurrency ?? model.portfolio.currency]),
|
|
483
|
-
...(asOfDate ? [asOfDate] : []),
|
|
536
|
+
...(coverageText ? [coverageText] : []),
|
|
537
|
+
...(asOfDate && footerEvidence ? [footerEvidence] : []),
|
|
538
|
+
// The coverage names the currency of its basket value; a non-USD account's returns say what they converted into.
|
|
539
|
+
...(returnsCurrency ? [returnsCurrency] : coverageText ? [] : [model.portfolio.currency]),
|
|
484
540
|
...(EVIDENCE_VIEWS.has(view) && model.evidence ? [model.evidence.source] : []),
|
|
485
541
|
]),
|
|
486
|
-
...(frozen ? ["snapshot"] : []),
|
|
487
542
|
].join(" · ") || null;
|
|
543
|
+
// What the figures are read from and to when: the basket's daily closes, or the marks and evidence of that day.
|
|
544
|
+
// The shocks alone are repriced at the current marks.
|
|
545
|
+
const sourceText = basketBody || !asOfDate
|
|
546
|
+
? null
|
|
547
|
+
: DAILY_CLOSE_VIEWS.has(view) && !stressShortfall ? `daily closes to ${asOfDate}` : `as of ${asOfDate}`;
|
|
488
548
|
usePaneNoticeFooter({
|
|
489
549
|
registrationId: "portfolio-risk-notices",
|
|
490
550
|
notices,
|
|
@@ -505,12 +565,13 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
505
565
|
},
|
|
506
566
|
]
|
|
507
567
|
: []),
|
|
508
|
-
//
|
|
509
|
-
...(
|
|
510
|
-
? [{
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
568
|
+
// Coverage first: a narrow footer cuts the method and the date before what the numbers cover.
|
|
569
|
+
...(asOfText
|
|
570
|
+
? [{ id: "asof", parts: [{ text: asOfText, tone: "muted" as const }] }]
|
|
571
|
+
: []),
|
|
572
|
+
// The source and its date, which a capture's own dated status line stands in for.
|
|
573
|
+
...(sourceText
|
|
574
|
+
? [{ id: "source", asOf: true, parts: [{ text: sourceText, tone: "muted" as const }] }]
|
|
514
575
|
: []),
|
|
515
576
|
...(actionStatus
|
|
516
577
|
? [
|
|
@@ -523,7 +584,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
523
584
|
],
|
|
524
585
|
hints,
|
|
525
586
|
}),
|
|
526
|
-
[resource.loading,
|
|
587
|
+
[resource.loading, asOfText, sourceText, actionStatus, hints],
|
|
527
588
|
);
|
|
528
589
|
useShortcut(
|
|
529
590
|
(event) => {
|
|
@@ -611,7 +672,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
611
672
|
? []
|
|
612
673
|
: [{ id: "asOf", label: "As of", width: 11, align: "left" as const }]),
|
|
613
674
|
// Attribution's evidence is the three effects, now columns of their own.
|
|
614
|
-
...(width >= 95 && !
|
|
675
|
+
...(width >= 95 && !footerEvidence && !attributionParts
|
|
615
676
|
? [
|
|
616
677
|
{
|
|
617
678
|
id: "detail",
|
|
@@ -623,7 +684,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
|
|
|
623
684
|
]
|
|
624
685
|
: []),
|
|
625
686
|
],
|
|
626
|
-
[width, view, valueHeader, sharedUnit, attributionParts,
|
|
687
|
+
[width, view, valueHeader, sharedUnit, attributionParts, footerEvidence, showPercentile, sharedDate, rows],
|
|
627
688
|
);
|
|
628
689
|
const selectView = (value: string) => {
|
|
629
690
|
setView(value);
|
|
@@ -10,6 +10,7 @@ import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginAppActi
|
|
|
10
10
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
11
11
|
import type { PaneProps } from "../../../types/plugin";
|
|
12
12
|
import { Box, ScrollBox, Text, TextAttributes, useRendererHost, useUiCapabilities } from "../../../ui";
|
|
13
|
+
import { withCountryFlag } from "../../../components/ui/country-flag";
|
|
13
14
|
import { SignInWall } from "../cloud/auth-actions";
|
|
14
15
|
import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
|
|
15
16
|
import { useOpenDescription } from "../shared/open-description";
|
|
@@ -173,13 +174,16 @@ export function AttentionPane({ width, height, focused }: PaneProps) {
|
|
|
173
174
|
color: row.zScore >= 2 ? colors.warning : row.zScore < 0 ? colors.textDim : colors.text, attributes: row.zScore >= 2 ? TextAttributes.BOLD : 0, keepColorWhenSelected: row.zScore >= 2 };
|
|
174
175
|
case "priceChangePct": return row.priceChangePct == null ? missingCell(colors) : { text: rowCell(row, column.id), value: row.priceChangePct, color: signedColor(row.priceChangePct, colors) };
|
|
175
176
|
case "relativeVolume": return row.relativeVolume == null ? missingCell(colors) : { text: rowCell(row, column.id), value: row.relativeVolume, color: row.relativeVolume >= 2 ? colors.textBright : colors.text };
|
|
176
|
-
case "sector":
|
|
177
|
+
case "sector": return { text: rowCell(row, column.id), color: colors.textDim };
|
|
178
|
+
// The column has room for the flag beside a code.
|
|
179
|
+
case "country": return withCountryFlag({ text: rowCell(row, column.id), color: colors.textDim }, row.country, desktop);
|
|
177
180
|
default: return { text: rowCell(row, column.id), value: numeric, color: colors.text };
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
183
|
const value = row[column.id as keyof AttentionGroup];
|
|
181
|
-
|
|
184
|
+
const groupCell: DataTableCell = { text: typeof value === "number" ? number(value) : value, value: typeof value === "number" ? value : undefined,
|
|
182
185
|
color: column.id === "name" ? colors.textBright : column.id === "tickers" ? colors.textDim : colors.text };
|
|
186
|
+
return tab === "countries" && column.id === "name" ? withCountryFlag(groupCell, row.name, desktop) : groupCell;
|
|
183
187
|
};
|
|
184
188
|
// The whole board in four figures; a preview would only summarise its own three rows.
|
|
185
189
|
const top = (groups: readonly AttentionGroup[]) => groups[0] ? { value: groups[0].name, detail: `${number(groups[0].sharePct, 1)}%` } : null;
|
|
@@ -4,7 +4,7 @@ import type { HeadlessBundleSection, HeadlessPaneColumn, HeadlessPaneContext, He
|
|
|
4
4
|
import { DATE_OPTION_EXAMPLES, isoDateOption } from "../../../utils/calendar-date";
|
|
5
5
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
6
6
|
import { fetchAward, fetchAwards } from "./client";
|
|
7
|
-
import { AWARD_TABS, awardScope, awardsPreviewNote, awardTypeLabel, futureAwardDatesNote, money } from "./model";
|
|
7
|
+
import { AWARD_TABS, awardScope, awardsCoverageNotes, awardsEmptyReason, awardsPreviewNote, awardTypeLabel, futureAwardDatesNote, money } from "./model";
|
|
8
8
|
import { REPORT_PAGE, REPORT_ROWS } from "../shared/headless-options";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -113,6 +113,8 @@ export const awardsHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
113
113
|
const missing = filter.ticker && (data.locked || !data.rows.length) ? await notATicker(filter.ticker, ctx) : null;
|
|
114
114
|
const lock = awardsPreviewNote(data, filter);
|
|
115
115
|
const futureDates = futureAwardDatesNote(data.rows, data.generatedAt);
|
|
116
|
+
const notes = [...awardsCoverageNotes(data), ...(futureDates ? [futureDates] : [])];
|
|
117
|
+
const empty = awardsEmptyReason(data);
|
|
116
118
|
const sections: HeadlessBundleSection[] = [];
|
|
117
119
|
const tab = String(args.options.tab ?? "feed");
|
|
118
120
|
if (tab === "feed" || tab === "company" || tab === "all") sections.push({ title: "Awards", columns: FEED_COLUMNS, rows: data.rows.map(awardRecord) });
|
|
@@ -122,8 +124,10 @@ export const awardsHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
122
124
|
{ title: "Sector totals", columns: aggregateColumns("Sector"), rows: data.sectors.map((row) => ({ ...row })) });
|
|
123
125
|
if (tab === "events" || tab === "all") sections.push({ title: "Awards relative to annual revenue", columns: EVENT_COLUMNS, rows: data.alerts.map(awardRecord) });
|
|
124
126
|
return { complete: !data.locked && !data.nextCursor && data.status === "available" && !Object.values(data.truncated ?? {}).some(Boolean), sections,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
// Collection health is what the awards may be missing, a coverage note; only a failure is an error.
|
|
128
|
+
...(notes.length ? { notes } : {}),
|
|
129
|
+
...(empty ? { empty } : {}),
|
|
130
|
+
errors: [...(missing ? [missing] : []), ...(lock ? [lock] : [])],
|
|
127
131
|
// A preview is the same latest awards whatever was asked, so the filters were requested, not applied.
|
|
128
132
|
metadata: { generatedAt: data.generatedAt, asOf: data.asOf, access: data.access, nextCursor: data.nextCursor, truncated: data.truncated, coverage: data.coverage, sources: data.sources,
|
|
129
133
|
filters: filter, filtersApplied: !data.locked } };
|
|
@@ -67,6 +67,24 @@ export function awardsPreviewNote(data: AwardsPayload, filter: AwardFilter): str
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/** The server's word for a selection with no awards: the report's empty line, not a gap in coverage. */
|
|
71
|
+
const NO_AWARDS = "No awards are available for this selection.";
|
|
72
|
+
|
|
73
|
+
/** Why the awards asked for are empty, as the server says it; null while there are some. */
|
|
74
|
+
export function awardsEmptyReason(data: Pick<AwardsPayload, "gaps">): string | null {
|
|
75
|
+
return data.gaps.includes(NO_AWARDS) ? NO_AWARDS : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Collection health (jurisdictions awaiting a first collection, unfinished
|
|
80
|
+
* windows, quarantined records, a paused or capped store): what the awards
|
|
81
|
+
* shown may be missing, so coverage notes in the pane, `fn AWARDS` and its
|
|
82
|
+
* JSON, never errors.
|
|
83
|
+
*/
|
|
84
|
+
export function awardsCoverageNotes(data: Pick<AwardsPayload, "gaps">): string[] {
|
|
85
|
+
return data.gaps.filter((gap) => gap !== NO_AWARDS);
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
/** Award dates after the response's own date are the source's, not a mapping slip; they say so instead of passing as past awards. */
|
|
71
89
|
export function futureAwardDatesNote(rows: readonly AwardRow[], generatedAt: string): string | null {
|
|
72
90
|
const today = generatedAt.slice(0, 10);
|
|
@@ -12,6 +12,7 @@ import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
|
|
|
12
12
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
13
13
|
import type { PaneProps } from "../../../types/plugin";
|
|
14
14
|
import { Box, Text, TextAttributes, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
|
|
15
|
+
import { withCountryFlag } from "../../../components/ui/country-flag";
|
|
15
16
|
import { publicTickerKey } from "../../../utils/exchanges";
|
|
16
17
|
import { truncateToDisplayWidth } from "../../../utils/format";
|
|
17
18
|
import { SignInWall } from "../cloud/auth-actions";
|
|
@@ -23,7 +24,7 @@ import { AwardDetail } from "./detail";
|
|
|
23
24
|
import { useAwardAlerts } from "./alerts";
|
|
24
25
|
import { AWARDS_UNAVAILABLE, fetchAwards, loadAward, loadAwards, validateAwards } from "./client";
|
|
25
26
|
import { useAwardsEvidence } from "./evidence";
|
|
26
|
-
import { AWARD_TABS, aggregateId, awardHistorySeries, awardPercent, awardPeriod, awardScope, awardsPreviewNote, awardTab, awardTypeLabel, futureAwardDatesNote, money,
|
|
27
|
+
import { AWARD_TABS, aggregateId, awardHistorySeries, awardPercent, awardPeriod, awardScope, awardsCoverageNotes, awardsPreviewNote, awardTab, awardTypeLabel, futureAwardDatesNote, money,
|
|
27
28
|
rankedAggregates } from "./model";
|
|
28
29
|
|
|
29
30
|
type Item = { kind: "award"; row: AwardRow } | { kind: "aggregate"; row: AwardAggregate } | { kind: "leader"; row: AwardCompanyLeader } | { kind: "locked"; id: string }
|
|
@@ -186,7 +187,8 @@ function AwardsView({ width, height, focused, symbol }: PaneProps & { symbol: st
|
|
|
186
187
|
error: data && !denied ? failed?.message ?? null : null, stale: loaded?.stale,
|
|
187
188
|
info: data?.asOf ? [{ id: "asof", parts: [{ text: `as of ${data.asOf.slice(0, 10)}`, tone: "muted" }] }] : [], hints });
|
|
188
189
|
usePaneNoticeFooter({ registrationId: "awards:notices", focused, notices: [
|
|
189
|
-
|
|
190
|
+
// The same coverage notes `fn AWARDS` prints: what the awards shown may be missing.
|
|
191
|
+
...(data ? awardsCoverageNotes(data) : []),
|
|
190
192
|
...(loaded?.refreshError ? [loaded.refreshError] : []),
|
|
191
193
|
...(data?.truncated && Object.values(data.truncated).some(Boolean) ? ["Some aggregate history exceeds this response's limit. Narrow the date range or scope."] : []),
|
|
192
194
|
...(tab === "company" && currencies.length > 1 && !currency ? [`Chart in ${chartCurrency}; select a currency to inspect other obligations.`] : []),
|
|
@@ -269,7 +271,8 @@ function AwardsView({ width, height, focused, symbol }: PaneProps & { symbol: st
|
|
|
269
271
|
if (column.id === "recipient") return { text: award.recipient.name, color: colors.textBright };
|
|
270
272
|
if (column.id === "ticker") return award.entity ? { text: award.entity.ticker, content: <Badge label={award.entity.ticker} /> } : missingCell(colors);
|
|
271
273
|
if (column.id === "agency") return { text: award.agency.name, color: colors.text };
|
|
272
|
-
|
|
274
|
+
// The column has room for the flag beside a code.
|
|
275
|
+
if (column.id === "country") return withCountryFlag({ text: award.jurisdiction, color: colors.textDim }, award.jurisdiction, desktop);
|
|
273
276
|
if (column.id === "period") return { text: awardPeriod(award), color: colors.textDim };
|
|
274
277
|
if (column.id === "title") return { text: award.title, color: colors.text };
|
|
275
278
|
if (column.id === "ratio") return { text: awardPercent(award), value: award.revenueComparison?.percent ?? null, color: colors.warning, attributes: TextAttributes.BOLD, keepColorWhenSelected: true };
|
|
@@ -5,7 +5,7 @@ import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
|
5
5
|
import { createBacktestDependencies, loadBacktestHistory } from "./client";
|
|
6
6
|
import { BACKTEST_PRESETS, DEFAULT_PRESET, lookbackYears, resolveRules } from "./presets";
|
|
7
7
|
import { BACKTEST_FRESHNESS, backtestPriceUnit, backtestReport } from "./report";
|
|
8
|
-
import { parseRule } from "./rules";
|
|
8
|
+
import { parseRule, RULE_SYNTAX } from "./rules";
|
|
9
9
|
import { runHistoryBacktest } from "./window";
|
|
10
10
|
|
|
11
11
|
/** The listing's price unit, as `history` reads it from the quote; null when no quote says. */
|
|
@@ -39,8 +39,8 @@ export const backtestHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
39
39
|
options: [
|
|
40
40
|
{ key: "preset", type: "enum", defaultValue: DEFAULT_PRESET.id, description: "Strategy preset, or custom to use --entry and --exit",
|
|
41
41
|
values: [...BACKTEST_PRESETS.map(({ id }) => ({ value: id })), { value: "custom" }] },
|
|
42
|
-
{ key: "entry", type: "string", defaultValue: "", description:
|
|
43
|
-
{ key: "exit", type: "string", defaultValue: "", description: "Custom exit rule, for example 'close < sma(200)'" },
|
|
42
|
+
{ key: "entry", type: "string", defaultValue: "", description: `Custom entry rule, for example 'close > sma(200)' or 'weekday == 3'. ${RULE_SYNTAX}` },
|
|
43
|
+
{ key: "exit", type: "string", defaultValue: "", description: "Custom exit rule, for example 'close < sma(200)' or 'weekday == 4'. Same operands and comparisons as --entry" },
|
|
44
44
|
{ key: "lookback", type: "enum", defaultValue: "10", values: [{ value: "5" }, { value: "10" }, { value: "max" }], description: "Evaluation years after indicator warmup" },
|
|
45
45
|
{ key: "cost", type: "integer", defaultValue: 5, minimum: 0, maximum: 100, description: "Cost per side in basis points" },
|
|
46
46
|
{ key: "end", type: "string", defaultValue: "", placeholder: "date", example: "--end 2026-10-16 --json",
|
|
@@ -14,9 +14,11 @@ export interface BacktestBar {
|
|
|
14
14
|
export type Operand =
|
|
15
15
|
| { kind: "number"; value: number }
|
|
16
16
|
| { kind: "field"; field: "close" | "open" | "high" | "low" }
|
|
17
|
+
| { kind: "calendar"; name: CalendarName }
|
|
17
18
|
| { kind: "indicator"; name: IndicatorName; args: number[] };
|
|
18
19
|
type IndicatorName = "sma" | "ema" | "rsi" | "macd" | "macd_signal" | "bb_upper" | "bb_lower" | "highest" | "lowest";
|
|
19
|
-
type
|
|
20
|
+
type CalendarName = "weekday" | "day_of_month";
|
|
21
|
+
type Comparator = ">" | "<" | ">=" | "<=" | "==" | "!=" | "crosses above" | "crosses below";
|
|
20
22
|
interface Condition { left: Operand; comparator: Comparator; right: Operand }
|
|
21
23
|
/** Conditions are ANDed. */
|
|
22
24
|
export type Rule = Condition[];
|
|
@@ -35,6 +37,11 @@ const INDICATORS: Record<IndicatorName, IndicatorSpec> = {
|
|
|
35
37
|
lowest: { defaults: [20], bounds: [PERIOD], usage: "lowest(n)" },
|
|
36
38
|
};
|
|
37
39
|
const FIELDS = new Set(["close", "open", "high", "low"]);
|
|
40
|
+
/** The session's UTC calendar date as a number, so a rule can trade a day of the week or of the month. */
|
|
41
|
+
const CALENDAR: Record<CalendarName, { min: number; max: number; values: string; of: (date: Date) => number }> = {
|
|
42
|
+
weekday: { min: 1, max: 7, values: "1 Monday to 7 Sunday", of: (date) => (date.getUTCDay() + 6) % 7 + 1 },
|
|
43
|
+
day_of_month: { min: 1, max: 31, values: "1 to 31", of: (date) => date.getUTCDate() },
|
|
44
|
+
};
|
|
38
45
|
const COMPARATORS: Array<[RegExp, Comparator]> = [
|
|
39
46
|
[/^crosses\s+above$/, "crosses above"],
|
|
40
47
|
[/^crosses\s+below$/, "crosses below"],
|
|
@@ -42,16 +49,29 @@ const COMPARATORS: Array<[RegExp, Comparator]> = [
|
|
|
42
49
|
[/^(<|below)$/, "<"],
|
|
43
50
|
[/^>=$/, ">="],
|
|
44
51
|
[/^<=$/, "<="],
|
|
52
|
+
[/^==?$/, "=="],
|
|
53
|
+
[/^!=$/, "!="],
|
|
45
54
|
];
|
|
46
55
|
|
|
56
|
+
/** Every operand a rule takes, as an error and `--help` list them. */
|
|
57
|
+
const RULE_OPERANDS = [
|
|
58
|
+
"close", "open", "high", "low", "a number",
|
|
59
|
+
...Object.entries(CALENDAR).map(([name, entry]) => `${name} (${entry.values})`),
|
|
60
|
+
...Object.values(INDICATORS).map((entry) => entry.usage),
|
|
61
|
+
].join(", ");
|
|
62
|
+
const RULE_COMPARISONS = ">, <, >=, <=, ==, !=, crosses above, crosses below";
|
|
63
|
+
/** What a rule is written from, for a message that sends the reader back to the syntax. */
|
|
64
|
+
export const RULE_SYNTAX = `Operands: ${RULE_OPERANDS}. Comparisons: ${RULE_COMPARISONS}. Join conditions with "and".`;
|
|
65
|
+
|
|
47
66
|
function parseOperand(text: string): Operand {
|
|
48
67
|
const value = text.trim().toLowerCase();
|
|
49
68
|
if (/^[+-]?(\d+(\.\d*)?|\.\d+)$/.test(value)) return { kind: "number", value: Number(value) };
|
|
50
69
|
if (FIELDS.has(value)) return { kind: "field", field: value as "close" };
|
|
70
|
+
if (Object.hasOwn(CALENDAR, value)) return { kind: "calendar", name: value as CalendarName };
|
|
51
71
|
const match = /^([a-z_]+)\s*(?:\(([^)]*)\))?$/.exec(value);
|
|
52
72
|
const name = match?.[1] as IndicatorName | undefined;
|
|
53
73
|
const spec = name ? INDICATORS[name] : undefined;
|
|
54
|
-
if (!match || !name || !spec) throw new Error(`Unknown operand "${text.trim()}". Use
|
|
74
|
+
if (!match || !name || !spec) throw new Error(`Unknown operand "${text.trim()}". Use ${RULE_OPERANDS}.`);
|
|
55
75
|
const given = match[2]?.trim() ? match[2].split(",").map((part) => part.trim()) : [];
|
|
56
76
|
if (given.length > spec.defaults.length) throw new Error(`${spec.usage} takes at most ${spec.defaults.length} values.`);
|
|
57
77
|
const args = spec.defaults.map((fallback, index) => {
|
|
@@ -72,12 +92,19 @@ export function parseRule(text: string): Rule {
|
|
|
72
92
|
const source = text.trim();
|
|
73
93
|
if (!source) throw new Error("Enter a rule, for example: sma(50) crosses above sma(200)");
|
|
74
94
|
return source.split(/\s+and\s+/i).map((part) => {
|
|
75
|
-
const match = /^(.+?)\s+(crosses\s+above|crosses\s+below
|
|
76
|
-
if (!match) throw new Error(`Cannot read "${part.trim()}". Write <operand> <comparison> <operand>, for example close > sma(200)
|
|
95
|
+
const match = /^(.+?)\s+(crosses\s+above|crosses\s+below|>=|<=|==|!=|=|>|<|above|below)\s+(.+)$/i.exec(part.trim());
|
|
96
|
+
if (!match) throw new Error(`Cannot read "${part.trim()}". Write <operand> <comparison> <operand>, for example close > sma(200). ${RULE_SYNTAX}`);
|
|
77
97
|
const comparator = COMPARATORS.find(([pattern]) => pattern.test(match[2]!.toLowerCase().replace(/\s+/g, " ")))![1];
|
|
78
98
|
const left = parseOperand(match[1]!);
|
|
79
99
|
const right = parseOperand(match[3]!);
|
|
80
|
-
if (left.kind === "number" && right.kind === "number") throw new Error("Compare at least one price or indicator.");
|
|
100
|
+
if (left.kind === "number" && right.kind === "number") throw new Error("Compare at least one price, date or indicator.");
|
|
101
|
+
for (const [calendar, other] of [[left, right], [right, left]] as const) {
|
|
102
|
+
if (calendar.kind !== "calendar" || other.kind !== "number") continue;
|
|
103
|
+
const { min, max, values } = CALENDAR[calendar.name];
|
|
104
|
+
if (!Number.isInteger(other.value) || other.value < min || other.value > max) {
|
|
105
|
+
throw new Error(`${calendar.name} is ${values}, not ${other.value}.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
81
108
|
return { left, comparator, right };
|
|
82
109
|
});
|
|
83
110
|
}
|
|
@@ -105,6 +132,13 @@ export function evaluateOperand(operand: Operand, bars: readonly BacktestBar[]):
|
|
|
105
132
|
const length = bars.length;
|
|
106
133
|
if (operand.kind === "number") return new Float64Array(length).fill(operand.value);
|
|
107
134
|
if (operand.kind === "field") return Float64Array.from(bars, (bar) => bar[operand.field] ?? Number.NaN);
|
|
135
|
+
if (operand.kind === "calendar") {
|
|
136
|
+
// A session's date is its UTC calendar day; a bar without a readable one has no weekday.
|
|
137
|
+
return Float64Array.from(bars, (bar) => {
|
|
138
|
+
const time = Date.parse(`${bar.date.slice(0, 10)}T00:00:00Z`);
|
|
139
|
+
return Number.isFinite(time) ? CALENDAR[operand.name].of(new Date(time)) : Number.NaN;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
108
142
|
const closes = bars.map((bar) => bar.close);
|
|
109
143
|
const [a = 0, b = 0, c = 0] = operand.args;
|
|
110
144
|
switch (operand.name) {
|
|
@@ -161,6 +195,8 @@ export function evaluateRule(rule: Rule, bars: readonly BacktestBar[]): boolean[
|
|
|
161
195
|
else if (condition.comparator === "<") met = l < r;
|
|
162
196
|
else if (condition.comparator === ">=") met = l >= r;
|
|
163
197
|
else if (condition.comparator === "<=") met = l <= r;
|
|
198
|
+
else if (condition.comparator === "==") met = l === r;
|
|
199
|
+
else if (condition.comparator === "!=") met = l !== r;
|
|
164
200
|
else {
|
|
165
201
|
const pl = left[index - 1], pr = right[index - 1];
|
|
166
202
|
met = pl !== undefined && pr !== undefined && !Number.isNaN(pl) && !Number.isNaN(pr)
|
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore
|
|
|
2
2
|
import { apiClient } from "../../../api-client";
|
|
3
3
|
import {
|
|
4
4
|
brokerMethodLabel,
|
|
5
|
+
brokerDirectorySummary,
|
|
5
6
|
brokerMethodSummary,
|
|
6
7
|
buildBrokerDirectory,
|
|
7
8
|
type BrokerDirectoryEntry,
|
|
@@ -39,7 +40,7 @@ import { isPlainKey } from "../../../utils/keyboard";
|
|
|
39
40
|
import { usePluginAppActions, usePluginBrokerActions } from "../../runtime";
|
|
40
41
|
import { BrokerProfileForm, profileFieldWidth, type BrokerEditKey } from "./detail";
|
|
41
42
|
import { useBrokerManagerKeyboard } from "./keyboard";
|
|
42
|
-
import type { BrokerManagerMessage } from "./pane-actions";
|
|
43
|
+
import type { BrokerManagerMessage, BrokerRowNote } from "./pane-actions";
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Adding a profile, one step at a time in the pane's detail: the broker, how
|
|
@@ -84,10 +85,13 @@ let attemptSequence = 0;
|
|
|
84
85
|
export function useBrokerAddFlow({
|
|
85
86
|
setBusy,
|
|
86
87
|
setMessage,
|
|
88
|
+
setRowNote,
|
|
87
89
|
onProfileAdded,
|
|
88
90
|
}: {
|
|
89
91
|
setBusy: (busy: string | null) => void;
|
|
90
92
|
setMessage: (message: BrokerManagerMessage | null) => void;
|
|
93
|
+
/** Says on the new profile's row why its first sync failed. */
|
|
94
|
+
setRowNote: (profileId: string, note: BrokerRowNote | null) => void;
|
|
91
95
|
/** Lands the pane on the profile the flow made, with the flow closed. */
|
|
92
96
|
onProfileAdded: (instanceId: string) => void;
|
|
93
97
|
}) {
|
|
@@ -95,7 +99,7 @@ export function useBrokerAddFlow({
|
|
|
95
99
|
const dispatch = useAppDispatch();
|
|
96
100
|
const getState = useAppGetState();
|
|
97
101
|
const { notify } = usePluginAppActions();
|
|
98
|
-
const { listBrokerAdapters, createBrokerInstance, syncBrokerInstance } = usePluginBrokerActions();
|
|
102
|
+
const { listBrokerAdapters, createBrokerInstance, updateBrokerInstance, syncBrokerInstance } = usePluginBrokerActions();
|
|
99
103
|
const [flow, setFlowState] = useState<BrokerAddFlowState | null>(null);
|
|
100
104
|
// Async work reads the flow as it is now, not as it was when it started.
|
|
101
105
|
const flowRef = useRef<BrokerAddFlowState | null>(null);
|
|
@@ -165,13 +169,13 @@ export function useBrokerAddFlow({
|
|
|
165
169
|
if (portfolio) showCollectionInPortfolioPane(state, dispatch, portfolio.id);
|
|
166
170
|
notify({ body: t("Connected! Positions will sync automatically."), type: "success" });
|
|
167
171
|
} catch (error) {
|
|
168
|
-
// The profile stays: its
|
|
172
|
+
// The profile stays: its row says why it did not sync.
|
|
169
173
|
land();
|
|
170
|
-
|
|
174
|
+
setRowNote(instanceId, { tone: "error", status: t("Sync failed"), text: errorText(error, tf("Failed to sync {label}.", { label })) });
|
|
171
175
|
} finally {
|
|
172
176
|
setBusy(null);
|
|
173
177
|
}
|
|
174
|
-
}, [dispatch, getState, notify, onProfileAdded, setBusy, setFlow,
|
|
178
|
+
}, [dispatch, getState, notify, onProfileAdded, setBusy, setFlow, setRowNote, syncBrokerInstance]);
|
|
175
179
|
|
|
176
180
|
const connectDevice = useCallback(async () => {
|
|
177
181
|
const current = flowRef.current;
|
|
@@ -240,6 +244,7 @@ export function useBrokerAddFlow({
|
|
|
240
244
|
connected = await connectSignedInBrokerProfile(broker, {
|
|
241
245
|
getConfig: () => getState().config,
|
|
242
246
|
createBrokerInstance,
|
|
247
|
+
enableBrokerInstance: (instanceId) => updateBrokerInstance(instanceId, {}, { enabled: true }),
|
|
243
248
|
// The first sync runs below, with its progress in the pane.
|
|
244
249
|
syncBrokerInstance: async () => {},
|
|
245
250
|
// Gloom's sign-in over the pane when needed, again after a session Gloom refused.
|
|
@@ -278,7 +283,7 @@ export function useBrokerAddFlow({
|
|
|
278
283
|
} finally {
|
|
279
284
|
if (committing) committingRef.current = false;
|
|
280
285
|
}
|
|
281
|
-
}, [createBrokerInstance, dialog, getState, setMessage, setStep, showAttempt, syncNewProfile]);
|
|
286
|
+
}, [createBrokerInstance, dialog, getState, setMessage, setStep, showAttempt, syncNewProfile, updateBrokerInstance]);
|
|
282
287
|
|
|
283
288
|
const chooseMethod = useCallback((entry: BrokerDirectoryEntry, method: BrokerMethod) => {
|
|
284
289
|
const current = flowRef.current;
|
|
@@ -384,7 +389,7 @@ export function BrokerAddFlowContent({
|
|
|
384
389
|
label: entry.name,
|
|
385
390
|
// The desktop says how it connects under the name, beside its logo.
|
|
386
391
|
...(desktop
|
|
387
|
-
? { description:
|
|
392
|
+
? { description: brokerDirectorySummary(entry), leading: <LogoMark src={brokerDirectoryLogo(entry, getBrokerLogo)} name={entry.name} size="lead" /> }
|
|
388
393
|
: { detail: brokerMethodSummary(entry) }),
|
|
389
394
|
}))}
|
|
390
395
|
rowDescriptions={desktop}
|