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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
1
2
|
import { matchesNumberStep } from "../../../components/ui/number-step";
|
|
2
3
|
import type { BrokerAdapter, BrokerConnectionStatus } from "../../../types/broker";
|
|
3
4
|
import type { BrokerInstanceConfig } from "../../../types/config";
|
|
@@ -5,6 +6,7 @@ import type { Quote } from "../../../types/financials";
|
|
|
5
6
|
import type { BrokerAccount, BrokerOrder, BrokerOrderPreview, BrokerOrderRequest, BrokerTradingCapabilities } from "../../../types/trading";
|
|
6
7
|
import { optionContractIdentity } from "../../../utils/options";
|
|
7
8
|
import { isStrategyOrder, strategyQuote } from "./strategy";
|
|
9
|
+
import { checkLinkedOrder, restingEntryPrice } from "./linked-orders";
|
|
8
10
|
import { brokerOrderDone, brokerOrderStatus, orderEstimatePrice, ticketEditingPriceStep } from "./orders-data";
|
|
9
11
|
|
|
10
12
|
export interface BrokerTradingContext {
|
|
@@ -92,6 +94,8 @@ function refusal(error: unknown): string | undefined {
|
|
|
92
94
|
return error.message.trim().replace(/\.$/, "") || "The broker refused this action";
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
/** Terms the broker sets with its figures, or for having none, that the user accepts on the review: never skipped. */
|
|
98
|
+
function asksForConsent(preview: BrokerOrderPreview): boolean { return !!preview.disclosure || preview.impactSource === "unavailable"; }
|
|
95
99
|
function copy<T>(value: T): T { return structuredClone(value); }
|
|
96
100
|
function freeze<T>(value: T): T {
|
|
97
101
|
if (value && typeof value === "object") {
|
|
@@ -226,7 +230,9 @@ export class BrokerTradingController {
|
|
|
226
230
|
this.editable();
|
|
227
231
|
const original = this.modifyOrder;
|
|
228
232
|
if (original && (canonical(request.contract) !== canonical(original.contract) || canonical(request.legs ?? null) !== canonical(original.legs ?? null)
|
|
229
|
-
|| request.action !== original.action || request.accountId !== original.accountId || request.orderType !== original.orderType || request.tif !== original.tif
|
|
233
|
+
|| request.action !== original.action || request.accountId !== original.accountId || request.orderType !== original.orderType || request.tif !== original.tif
|
|
234
|
+
// One order of a bracket or an OCO pair changes alone; a trailing stop keeps its unit.
|
|
235
|
+
|| request.orderClass !== undefined || (request.trailingPercent !== undefined) !== (original.trailingPercent !== undefined))) {
|
|
230
236
|
throw new Error("A modification keeps the original account, instrument, legs, side, type and time in force.");
|
|
231
237
|
}
|
|
232
238
|
// A spread's legs are its instrument: other legs need another quote.
|
|
@@ -275,15 +281,20 @@ export class BrokerTradingController {
|
|
|
275
281
|
if (request.outsideRth && (!capabilities.extendedHours || (capabilities.extendedHoursTif && !capabilities.extendedHoursTif.includes(request.tif ?? "DAY")))) throw new Error("Extended hours are unavailable for this order and time in force.");
|
|
276
282
|
const limit = request.orderType === "LMT" || request.orderType === "STP LMT";
|
|
277
283
|
const stop = request.orderType === "STP" || request.orderType === "STP LMT";
|
|
278
|
-
|
|
279
|
-
if (needed && !positive(value)) throw new Error(`Enter a valid ${label} price.`);
|
|
280
|
-
if (!needed && value !== undefined) throw new Error(`Remove the ${label} price for this order type.`);
|
|
284
|
+
const checkPrice = (value: number) => {
|
|
281
285
|
const decimals = capabilities.priceDecimals;
|
|
282
|
-
if (
|
|
286
|
+
if (decimals !== undefined && (!Number.isInteger(decimals) || decimals < 0 || decimals > 18 || Number(value.toFixed(decimals)) !== value)) {
|
|
283
287
|
throw new Error("Price precision exceeds the broker's limit.");
|
|
284
288
|
}
|
|
285
|
-
if (
|
|
289
|
+
if (capabilities.priceStep !== undefined && !matchesNumberStep(value, capabilities.priceStep)) throw new Error("Price does not match the broker's allowed increment.");
|
|
290
|
+
};
|
|
291
|
+
for (const [needed, value, label] of [[limit, request.limitPrice, "limit"], [stop, request.stopPrice, "stop"]] as const) {
|
|
292
|
+
if (needed && !positive(value)) throw new Error(`Enter a valid ${label} price.`);
|
|
293
|
+
if (!needed && value !== undefined) throw new Error(`Remove the ${label} price for this order type.`);
|
|
294
|
+
if (value !== undefined) checkPrice(value);
|
|
286
295
|
}
|
|
296
|
+
// A bracket, an OCO pair or a trailing stop only where the broker runs it itself.
|
|
297
|
+
checkLinkedOrder(request, capabilities, context.adapter.describeInstance?.(context.instance).brokerName ?? context.adapter.name, checkPrice);
|
|
287
298
|
return copy(request);
|
|
288
299
|
}
|
|
289
300
|
|
|
@@ -355,7 +366,9 @@ export class BrokerTradingController {
|
|
|
355
366
|
// The stream answered what a quote request could not.
|
|
356
367
|
const answered = this.snapshot.error !== undefined && this.snapshot.error === this.quoteError;
|
|
357
368
|
this.publish({ quote: value, loadingQuote: false, ...(answered ? { error: undefined } : {}) });
|
|
358
|
-
} catch {
|
|
369
|
+
} catch {
|
|
370
|
+
// Intentional ignore: invalid streamed quotes leave the last validated ticket quote in place.
|
|
371
|
+
}
|
|
359
372
|
}
|
|
360
373
|
|
|
361
374
|
/** A streamed quote for the current draft, still being pushed, stands in for asking. */
|
|
@@ -437,9 +450,9 @@ export class BrokerTradingController {
|
|
|
437
450
|
this.entryPending = true;
|
|
438
451
|
try {
|
|
439
452
|
await this.prepareReview(fast);
|
|
440
|
-
// New risk information cannot be waived by a session preference.
|
|
453
|
+
// New risk information cannot be waived by a session preference, nor can the broker's terms.
|
|
441
454
|
const review = this.snapshot.review;
|
|
442
|
-
if (fast && review && !review.requiresTypedConfirmation && !review.warnings.length && !review.preview.errors?.length) await this.submitReviewed(undefined, true);
|
|
455
|
+
if (fast && review && !review.requiresTypedConfirmation && !review.warnings.length && !review.preview.errors?.length && !asksForConsent(review.preview)) await this.submitReviewed(undefined, true);
|
|
443
456
|
} finally { this.entryPending = false; }
|
|
444
457
|
}
|
|
445
458
|
|
|
@@ -473,6 +486,19 @@ export class BrokerTradingController {
|
|
|
473
486
|
&& (request.action === "SELL" ? request.stopPrice >= touch : request.stopPrice <= touch)) {
|
|
474
487
|
warnings.push(request.action === "SELL" ? "This sell stop is at or above the bid, so it triggers at once." : "This buy stop is at or below the ask, so it triggers at once.");
|
|
475
488
|
}
|
|
489
|
+
// An exit already through the market trades the moment it works: an OCO's at once, a bracket's once the entry fills.
|
|
490
|
+
// A bracket's exits trade the other way from its entry; an OCO's two orders trade its own way. An entry resting away
|
|
491
|
+
// from the market fills at its own price, and its exits were checked against that: only one that trades now, at
|
|
492
|
+
// the market, can leave an exit through it.
|
|
493
|
+
const exitSells = request.orderClass === "oco" ? request.action === "SELL" : request.action === "BUY";
|
|
494
|
+
const exitTouch = request.orderClass === "bracket" && restingEntryPrice(request, quote.quote) !== undefined ? undefined
|
|
495
|
+
: exitSells ? quote.quote.bid : quote.quote.ask;
|
|
496
|
+
if (request.orderClass && positive(exitTouch) && positive(request.stopLossPrice) && (exitSells ? request.stopLossPrice >= exitTouch : request.stopLossPrice <= exitTouch)) {
|
|
497
|
+
warnings.push(request.orderClass === "oco" ? `This ${exitSells ? "sell" : "buy"} stop is through the market, so it triggers at once.` : "The stop loss is through the market, so it triggers as soon as the entry fills.");
|
|
498
|
+
}
|
|
499
|
+
if (request.orderClass === "bracket" && positive(exitTouch) && positive(request.takeProfitPrice) && (exitSells ? request.takeProfitPrice <= exitTouch : request.takeProfitPrice >= exitTouch)) {
|
|
500
|
+
warnings.push("The take profit is through the market, so it fills as soon as the entry does.");
|
|
501
|
+
}
|
|
476
502
|
if (quote.quote.marketState === "CLOSED") warnings.push("The broker reports the market is closed. The order may wait for another session.");
|
|
477
503
|
// Legs whose quotes net the other way: a debit order that would be paid, or a credit order that would pay.
|
|
478
504
|
if (request.legs?.length && Number.isFinite(quote.quote.price) && quote.quote.price <= 0) warnings.push(request.action === "BUY"
|
|
@@ -482,7 +508,7 @@ export class BrokerTradingController {
|
|
|
482
508
|
const result: BrokerOrderReview = { request, preview: copy(preview), warnings: [...new Set(warnings)], account: copy(account), brokerName: context.adapter.name,
|
|
483
509
|
createdAt: this.now(), mode: this.modifyOrder ? "modify" : "place", requiresTypedConfirmation: account.tradingMode !== "simulation", confirmationText: request.contract.symbol.toUpperCase() };
|
|
484
510
|
this.gate = { context: fingerprint, request: canonical(request), version, quoteRisk: this.quoteRisk(quote), order: this.modifyOrder ? copy(this.modifyOrder) : undefined };
|
|
485
|
-
this.publish({ phase: skipConfirmation && !result.warnings.length && !preview.errors?.length ? "previewing" : "review", review: result, quote, error: preview.errors?.length ? "The broker preview has blocking errors." : undefined });
|
|
511
|
+
this.publish({ phase: skipConfirmation && !result.warnings.length && !preview.errors?.length && !asksForConsent(preview) ? "previewing" : "review", review: result, quote, error: preview.errors?.length ? "The broker preview has blocking errors." : undefined });
|
|
486
512
|
} catch (error) {
|
|
487
513
|
if (!this.disposed && version === this.version) this.publish({ phase: "editing", review: undefined, error: errorMessage(error, "The broker could not preview this order.") });
|
|
488
514
|
throw error;
|
|
@@ -537,7 +563,7 @@ export class BrokerTradingController {
|
|
|
537
563
|
const { context, capabilities } = this.context(true, request);
|
|
538
564
|
this.checkOrder(order, context, "modify");
|
|
539
565
|
if (!capabilities.modify || !context.adapter.modifyOrder) throw new Error("This broker does not support modifying this order.");
|
|
540
|
-
if (!["MKT", "LMT", "STP", "STP LMT"].includes(order.orderType)) throw new Error("This order type cannot be modified in the shared ticket.");
|
|
566
|
+
if (!["MKT", "LMT", "STP", "STP LMT", "TRAIL"].includes(order.orderType)) throw new Error("This order type cannot be modified in the shared ticket.");
|
|
541
567
|
this.modifyOrder = undefined;
|
|
542
568
|
this.setDraft({ ...request, brokerInstanceId: context.instance.id });
|
|
543
569
|
this.modifyOrder = copy(order);
|
|
@@ -561,7 +587,10 @@ export class BrokerTradingController {
|
|
|
561
587
|
|
|
562
588
|
private orderRequest(order: BrokerOrder): BrokerOrderRequest {
|
|
563
589
|
return { brokerInstanceId: order.brokerInstanceId, accountId: order.accountId, contract: copy(order.contract), action: order.action,
|
|
564
|
-
orderType: order.orderType as BrokerOrderRequest["orderType"], quantity: order.quantity,
|
|
590
|
+
orderType: order.orderType as BrokerOrderRequest["orderType"], quantity: order.quantity, tif: order.tif,
|
|
591
|
+
// A trailing stop's trigger is the broker's to move: a change sets its distance.
|
|
592
|
+
...(order.orderType === "TRAIL" ? order.trailingPercent !== undefined ? { trailingPercent: order.trailingPercent } : { trailingAmount: order.trailingAmount }
|
|
593
|
+
: { limitPrice: order.limitPrice, stopPrice: order.stopPrice }),
|
|
565
594
|
...(order.legs?.length ? { legs: copy(order.legs) } : {}) };
|
|
566
595
|
}
|
|
567
596
|
|
|
@@ -608,7 +637,10 @@ export class BrokerTradingController {
|
|
|
608
637
|
: await context.adapter.listOpenOrders?.(context.instance) ?? [];
|
|
609
638
|
return orders.find((value): value is BrokerOrder => !!value && value.accountId === order.accountId
|
|
610
639
|
&& value.contract.symbol === order.contract.symbol && (value.orderId === order.orderId || sameOrder(value, order)));
|
|
611
|
-
} catch {
|
|
640
|
+
} catch (error) {
|
|
641
|
+
logFailure("broker-trading.controller", "Look up broker order failed")(error);
|
|
642
|
+
return undefined;
|
|
643
|
+
}
|
|
612
644
|
}
|
|
613
645
|
|
|
614
646
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { startTransition, useCallback, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
1
|
+
import { startTransition, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
2
2
|
import { PaneFooterScope, usePaneMenuItems } from "../../../components";
|
|
3
3
|
import { usePaneAppConfig, usePaneStateValue } from "../../../state/app/context";
|
|
4
4
|
import { usePluginBrokerActions } from "../../runtime";
|
|
@@ -91,15 +91,33 @@ export function ResearchTradeWorkspace({ width, height, focused, symbol, tabId,
|
|
|
91
91
|
const ticketFocused = split && panelFocused;
|
|
92
92
|
// The chart consumes its own presses, so the box below never hears them: it says so itself.
|
|
93
93
|
const focusChart = useCallback(() => setPanelFocused(false), []);
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
// A price picked on the chart beside the ticket: the chart takes the keys until it is picked or cancelled.
|
|
95
|
+
const [pickingField, setPickingField] = useState<string>();
|
|
96
|
+
const picking = split ? projection.priceFields?.find((entry) => entry.field === pickingField) : undefined;
|
|
97
|
+
const pickPrice = useCallback((field?: string) => {
|
|
98
|
+
const choice = field ? projection.priceFields?.find((entry) => entry.field === field) : projection.priceFields?.[0];
|
|
99
|
+
if (!split || !choice) return;
|
|
100
|
+
captureTicket(false); setPickingField(choice.field); setPanelFocused(false);
|
|
101
|
+
}, [split, projection.priceFields, captureTicket]);
|
|
102
|
+
const cancelPick = useCallback(() => { setPickingField(undefined); setPanelFocused(true); }, []);
|
|
103
|
+
const finishPick = useCallback((value: number) => {
|
|
104
|
+
if (picking) projection.setPrice?.(picking.field, value);
|
|
105
|
+
cancelPick();
|
|
106
|
+
}, [picking, projection, cancelPick]);
|
|
107
|
+
// Back in the ticket, away from the pane, or a ticket that no longer has that price: the pick is over.
|
|
108
|
+
useEffect(() => { if (pickingField && (!focused || ticketFocused || !picking)) setPickingField(undefined); }, [pickingField, focused, ticketFocused, picking]);
|
|
109
|
+
const context = useMemo(() => ({ ...projection, symbol, available, docked, visible, ticketFocused, open: openTicket, close, focusChart, publish,
|
|
110
|
+
picking, pickPrice, finishPick, cancelPick, ...(available ? { orders } : {}) }),
|
|
111
|
+
[projection, symbol, available, docked, visible, ticketFocused, openTicket, close, focusChart, orders, picking, pickPrice, finishPick, cancelPick]);
|
|
96
112
|
useShortcut((event) => {
|
|
97
113
|
if (event.name !== "f6" || event.repeat) return;
|
|
98
114
|
event.preventDefault(); event.stopPropagation(); captureTicket(false); setPanelFocused((value) => !value);
|
|
99
115
|
}, { enabled: focused && split && !dialogOpen, allowEditable: true, phase: "before", scope: "research:trade-focus" });
|
|
100
116
|
useShortcut((event) => {
|
|
101
117
|
if (event.name !== "escape" || event.defaultPrevented || event.targetEditable) return;
|
|
102
|
-
event.preventDefault(); event.stopPropagation();
|
|
118
|
+
event.preventDefault(); event.stopPropagation();
|
|
119
|
+
// While the chart picks a price, Esc only ends the pick, here too when the chart has nothing drawn to take it.
|
|
120
|
+
if (pickingField) cancelPick(); else close();
|
|
103
121
|
}, { enabled: focused && split && !panelFocused && !dialogOpen, phase: "after", scope: "research:trade-close" });
|
|
104
122
|
|
|
105
123
|
// The divider between chart and ticket drags, and < > move it from the keyboard.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BrokerAdapter } from "../../../types/broker";
|
|
2
2
|
import type { HeadlessPaneContext, HeadlessPaneDefinition, HeadlessPaneColumn } from "../../../types/headless";
|
|
3
3
|
import { brokerOrderStatus, loadBrokerOrdersSnapshot } from "./orders-data";
|
|
4
|
+
import { accountsShownFor } from "../../../brokers/broker-portfolio-sync";
|
|
4
5
|
|
|
5
6
|
type BrokerResolver = (brokerType: string, context: HeadlessPaneContext) => BrokerAdapter | null;
|
|
6
7
|
|
|
@@ -44,7 +45,7 @@ function createBrokerOrdersHeadless(
|
|
|
44
45
|
if (!profiles.length) throw new Error("Connect a broker profile that reports orders in Brokers first.");
|
|
45
46
|
const { instance, broker } = profiles[ordinal(args.options.profile, profiles.length, "profile")]!;
|
|
46
47
|
if (!broker.listAccounts) throw new Error("This broker does not report account identities.");
|
|
47
|
-
const accounts = await broker.listAccounts(instance);
|
|
48
|
+
const accounts = accountsShownFor(context.config, instance, await broker.listAccounts(instance));
|
|
48
49
|
context.signal.throwIfAborted();
|
|
49
50
|
if (!accounts.length) throw new Error("This broker profile has no available accounts.");
|
|
50
51
|
const account = accounts[ordinal(args.options.account, accounts.length, "account")]!;
|
|
@@ -5,7 +5,11 @@ import { brokerCanTrade, createTradeCommand, openTradeTicket } from "./command";
|
|
|
5
5
|
import { brokerOrderFeed } from "./order-feed";
|
|
6
6
|
import { tickerSecType } from "./pane-model";
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Every enabled trading profile's orders, held for as long as the app runs:
|
|
10
|
+
* its fills toast with no ticket open, and sync the profile's positions into
|
|
11
|
+
* its portfolio.
|
|
12
|
+
*/
|
|
9
13
|
let releaseHeldOrders: (() => void) | undefined;
|
|
10
14
|
|
|
11
15
|
/** A ticker without a saved asset category trades as its research instrument kind says. */
|
|
@@ -28,6 +32,7 @@ export const brokerTradingModule: PluginModule = {
|
|
|
28
32
|
}],
|
|
29
33
|
setup(ctx) {
|
|
30
34
|
brokerOrderFeed.setNotifier((notification) => { ctx.notify(notification); });
|
|
35
|
+
brokerOrderFeed.setPositionSync((instanceId) => ctx.syncBrokerInstance(instanceId));
|
|
31
36
|
let held = new Map<string, { key: string; release(): void }>();
|
|
32
37
|
// A broker registered by a later plugin, or a profile added or edited, is held from then on.
|
|
33
38
|
const holdProfiles = () => {
|
|
@@ -50,6 +55,7 @@ export const brokerTradingModule: PluginModule = {
|
|
|
50
55
|
for (const entry of held.values()) entry.release();
|
|
51
56
|
held.clear();
|
|
52
57
|
brokerOrderFeed.setNotifier(undefined);
|
|
58
|
+
brokerOrderFeed.setPositionSync(undefined);
|
|
53
59
|
brokerOrderFeed.clear();
|
|
54
60
|
};
|
|
55
61
|
const supported = (symbol: string, secType = "STK") => brokerCanTrade(ctx.getConfig().brokerInstances, (type) => ctx.getBrokerAdapter?.(type), symbol, secType);
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Quote } from "../../../types/financials";
|
|
2
|
+
import type { BrokerOrder, BrokerOrderClass, BrokerOrderLinkRole, BrokerOrderRequest, BrokerTradingCapabilities, BrokerTrailingUnit } from "../../../types/trading";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Brackets, OCO pairs and trailing stops: what a broker takes, what a draft of
|
|
6
|
+
* one must hold, and how the orders it becomes are listed together. The broker
|
|
7
|
+
* places and runs them; nothing here makes one out of separate orders.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type LinkedFeature = BrokerOrderClass | "trailing";
|
|
11
|
+
|
|
12
|
+
const FEATURE_NAME: Record<LinkedFeature, string> = { bracket: "brackets", oco: "OCO orders", trailing: "trailing stops" };
|
|
13
|
+
|
|
14
|
+
/** Whether the broker takes a bracket, an OCO pair or a trailing stop here, and the refusal when it does not. */
|
|
15
|
+
export function linkedFeature(capabilities: BrokerTradingCapabilities | undefined, feature: LinkedFeature, brokerName: string): { available: boolean; reason?: string } {
|
|
16
|
+
const available = feature === "trailing"
|
|
17
|
+
? !!capabilities?.orderTypes.includes("TRAIL") && !!capabilities.trailing?.length
|
|
18
|
+
: !!capabilities?.orderClasses?.includes(feature);
|
|
19
|
+
if (available) return { available };
|
|
20
|
+
return { available, reason: `${brokerName} does not take ${FEATURE_NAME[feature]}.` };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The trailing unit a draft edits: percent once it holds one, else the amount. */
|
|
24
|
+
export function trailingUnit(request: Pick<BrokerOrderRequest, "trailingAmount" | "trailingPercent">): BrokerTrailingUnit {
|
|
25
|
+
return "trailingPercent" in request && !("trailingAmount" in request) ? "percent" : "amount";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const positive = (value: number | undefined): value is number => value !== undefined && Number.isFinite(value) && value > 0;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The entry's own price an exit is measured against: its limit, or its stop,
|
|
32
|
+
* or none for a market order.
|
|
33
|
+
*/
|
|
34
|
+
function entryPrice(request: BrokerOrderRequest): number | undefined {
|
|
35
|
+
if (request.orderType === "LMT" || request.orderType === "STP LMT") return request.limitPrice;
|
|
36
|
+
if (request.orderType === "STP") return request.stopPrice;
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The entry's price when it rests away from the market, where it fills: a buy
|
|
42
|
+
* limit under the ask or a buy stop over it, a sell the mirror. None for one
|
|
43
|
+
* that trades now, or without a quote.
|
|
44
|
+
*/
|
|
45
|
+
export function restingEntryPrice(request: BrokerOrderRequest, quote: Pick<Quote, "bid" | "ask">): number | undefined {
|
|
46
|
+
const buy = request.action === "BUY";
|
|
47
|
+
const touch = buy ? quote.ask : quote.bid;
|
|
48
|
+
if (!positive(touch)) return undefined;
|
|
49
|
+
const entry = entryPrice(request);
|
|
50
|
+
if (request.orderType === "LMT") return positive(entry) && (buy ? entry < touch : entry > touch) ? entry : undefined;
|
|
51
|
+
if (request.orderType === "STP" || request.orderType === "STP LMT") {
|
|
52
|
+
return positive(request.stopPrice) && (buy ? request.stopPrice > touch : request.stopPrice < touch) ? entry : undefined;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Checks a draft's bracket, OCO pair or trailing stop against what the broker
|
|
59
|
+
* declared, before any preview. Throws with the reason to show.
|
|
60
|
+
*/
|
|
61
|
+
export function checkLinkedOrder(request: BrokerOrderRequest, capabilities: BrokerTradingCapabilities, brokerName: string, checkPrice: (value: number, label: string) => void): void {
|
|
62
|
+
const buy = request.action === "BUY";
|
|
63
|
+
const trailing = request.orderType === "TRAIL";
|
|
64
|
+
if (!trailing && (request.trailingAmount !== undefined || request.trailingPercent !== undefined)) throw new Error("Remove the trailing distance for this order type.");
|
|
65
|
+
if (trailing) {
|
|
66
|
+
const feature = linkedFeature(capabilities, "trailing", brokerName);
|
|
67
|
+
if (!feature.available) throw new Error(feature.reason);
|
|
68
|
+
const unit = trailingUnit(request);
|
|
69
|
+
if ((request.trailingAmount !== undefined) === (request.trailingPercent !== undefined)) throw new Error("Enter the trailing distance in price or in percent.");
|
|
70
|
+
if (!capabilities.trailing?.includes(unit)) throw new Error(unit === "percent" ? `${brokerName} takes a trailing stop in price, not percent.` : `${brokerName} takes a trailing stop in percent, not price.`);
|
|
71
|
+
const distance = unit === "percent" ? request.trailingPercent : request.trailingAmount;
|
|
72
|
+
if (!positive(distance) || unit === "percent" && distance >= 100) throw new Error("Enter a valid trailing distance.");
|
|
73
|
+
if (unit === "amount") checkPrice(distance, "trailing");
|
|
74
|
+
}
|
|
75
|
+
const exits = request.takeProfitPrice !== undefined || request.stopLossPrice !== undefined;
|
|
76
|
+
if (!request.orderClass) {
|
|
77
|
+
if (exits) throw new Error("Remove the take profit and stop loss, or make the order a bracket.");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const feature = linkedFeature(capabilities, request.orderClass, brokerName);
|
|
81
|
+
if (!feature.available) throw new Error(feature.reason);
|
|
82
|
+
if (request.legs?.length) throw new Error("A spread cannot carry a bracket or an OCO pair.");
|
|
83
|
+
if (request.orderClass === "oco") {
|
|
84
|
+
if (request.orderType !== "LMT") throw new Error("An OCO pair is a limit and a stop.");
|
|
85
|
+
if (request.takeProfitPrice !== undefined) throw new Error("An OCO pair is a limit and a stop, with no take profit.");
|
|
86
|
+
if (!positive(request.stopLossPrice)) throw new Error("Enter a valid stop price.");
|
|
87
|
+
checkPrice(request.stopLossPrice, "stop");
|
|
88
|
+
// A sell pair exits a long: a limit above, a stop below. A buy pair is the mirror.
|
|
89
|
+
if (positive(request.limitPrice) && (buy ? request.limitPrice >= request.stopLossPrice : request.limitPrice <= request.stopLossPrice)) {
|
|
90
|
+
throw new Error(buy ? "A buy OCO's limit sits below its stop." : "A sell OCO's limit sits above its stop.");
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!["MKT", "LMT", "STP", "STP LMT"].includes(request.orderType)) throw new Error("A bracket's entry is a market, limit or stop order.");
|
|
95
|
+
if (!positive(request.takeProfitPrice) && !positive(request.stopLossPrice)) throw new Error("Enter a take profit, a stop loss or both.");
|
|
96
|
+
for (const [value, label] of [[request.takeProfitPrice, "take profit"], [request.stopLossPrice, "stop loss"]] as const) {
|
|
97
|
+
if (value === undefined) continue;
|
|
98
|
+
if (!positive(value)) throw new Error(`Enter a valid ${label} price.`);
|
|
99
|
+
checkPrice(value, label);
|
|
100
|
+
}
|
|
101
|
+
// A buy's take profit sells above where it bought and its stop loss below; a sell's the mirror.
|
|
102
|
+
const entry = entryPrice(request);
|
|
103
|
+
const above = (value: number, than: number) => buy ? value > than : value < than;
|
|
104
|
+
if (positive(entry) && positive(request.takeProfitPrice) && !above(request.takeProfitPrice, entry)) throw new Error(buy ? "The take profit must be above the entry." : "The take profit must be below the entry.");
|
|
105
|
+
if (positive(entry) && positive(request.stopLossPrice) && !above(entry, request.stopLossPrice)) throw new Error(buy ? "The stop loss must be below the entry." : "The stop loss must be above the entry.");
|
|
106
|
+
if (positive(request.takeProfitPrice) && positive(request.stopLossPrice) && !above(request.takeProfitPrice, request.stopLossPrice)) {
|
|
107
|
+
throw new Error(buy ? "The take profit must be above the stop loss." : "The take profit must be below the stop loss.");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const linkRoleName: Record<BrokerOrderLinkRole, string> = { entry: "Entry", takeProfit: "Take profit", stopLoss: "Stop loss" };
|
|
112
|
+
/** Beside a price in a one-line row. */
|
|
113
|
+
export const linkRoleShort: Record<BrokerOrderLinkRole, string> = { entry: "", takeProfit: "TP", stopLoss: "SL" };
|
|
114
|
+
|
|
115
|
+
/** "Trailing $1.50", "Trailing 2%": a trailing stop's distance. */
|
|
116
|
+
export function trailingText(order: Pick<BrokerOrder, "trailingAmount" | "trailingPercent">, money: (value: number) => string): string {
|
|
117
|
+
if (positive(order.trailingPercent)) return `Trailing ${order.trailingPercent.toLocaleString("en-US", { maximumFractionDigits: 4 })}%`;
|
|
118
|
+
return positive(order.trailingAmount) ? `Trailing ${money(order.trailingAmount)}` : "Trailing";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const ROLE_ORDER: Record<BrokerOrderLinkRole, number> = { entry: 0, takeProfit: 1, stopLoss: 2 };
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Orders as a list shows them: newest first, with each group's orders
|
|
125
|
+
* together where its newest one sits, the entry before its take profit and
|
|
126
|
+
* stop loss. `child` marks an exit listed under its entry.
|
|
127
|
+
*/
|
|
128
|
+
export function linkedOrderRows<T extends Pick<BrokerOrder, "linkGroup" | "linkRole" | "accountId">>(orders: readonly T[]): Array<{ order: T; child: boolean }> {
|
|
129
|
+
const groups = new Map<string, T[]>();
|
|
130
|
+
for (const order of orders) {
|
|
131
|
+
if (!order.linkGroup) continue;
|
|
132
|
+
const key = `${order.accountId ?? ""}:${order.linkGroup}`;
|
|
133
|
+
groups.set(key, [...groups.get(key) ?? [], order]);
|
|
134
|
+
}
|
|
135
|
+
const rows: Array<{ order: T; child: boolean }> = [];
|
|
136
|
+
const placed = new Set<string>();
|
|
137
|
+
for (const order of orders) {
|
|
138
|
+
const key = order.linkGroup ? `${order.accountId ?? ""}:${order.linkGroup}` : undefined;
|
|
139
|
+
const group = key ? groups.get(key) : undefined;
|
|
140
|
+
if (!key || !group || group.length < 2) { rows.push({ order, child: false }); continue; }
|
|
141
|
+
if (placed.has(key)) continue;
|
|
142
|
+
placed.add(key);
|
|
143
|
+
const sorted = [...group].sort((a, b) => ROLE_ORDER[a.linkRole ?? "entry"] - ROLE_ORDER[b.linkRole ?? "entry"]);
|
|
144
|
+
const entry = sorted.some((item) => item.linkRole === "entry");
|
|
145
|
+
for (const item of sorted) rows.push({ order: item, child: entry && item.linkRole !== "entry" });
|
|
146
|
+
}
|
|
147
|
+
return rows;
|
|
148
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
1
2
|
import { useEffect, useRef, useSyncExternalStore } from "react";
|
|
2
3
|
import type { BrokerAdapter } from "../../../types/broker";
|
|
3
4
|
import type { BrokerInstanceConfig } from "../../../types/config";
|
|
@@ -5,6 +6,7 @@ import type { AppNotificationRequest } from "../../../types/plugin";
|
|
|
5
6
|
import type { BrokerOrder } from "../../../types/trading";
|
|
6
7
|
import { brokerOrderDone, brokerOrderStatus } from "./orders-data";
|
|
7
8
|
import { orderInstrumentLabel } from "./strategy";
|
|
9
|
+
import { linkRoleName, trailingText } from "./linked-orders";
|
|
8
10
|
|
|
9
11
|
/** Reconcile with open orders this often while the broker pushes changes. */
|
|
10
12
|
const LIVE_RECONCILE_MS = 60_000;
|
|
@@ -19,6 +21,9 @@ const DONE_RETENTION_MS = 10 * 60_000;
|
|
|
19
21
|
/** An order first seen already ended is news only if it ended this recently. */
|
|
20
22
|
const RECENT_END_MS = 2 * 60_000;
|
|
21
23
|
const SEEN_LIMIT = 1_000;
|
|
24
|
+
/** A fill re-syncs its profile's positions this long after the last fill in a burst, and no later than the cap after the first. */
|
|
25
|
+
const FILL_SYNC_MS = 2_000;
|
|
26
|
+
const FILL_SYNC_MAX_WAIT_MS = 10_000;
|
|
22
27
|
|
|
23
28
|
export interface BrokerOrderFeedState {
|
|
24
29
|
/** Working orders, and those that ended while watched, newest first. */
|
|
@@ -68,8 +73,11 @@ export function orderToastBody(order: BrokerOrder): string {
|
|
|
68
73
|
const symbol = orderInstrumentLabel(order);
|
|
69
74
|
const qty = (value: number) => value.toLocaleString("en-US", { maximumFractionDigits: 4 });
|
|
70
75
|
const filled = order.filled > 0 && Number.isFinite(order.avgFillPrice) ? ` · ${qty(order.filled)} filled at ${order.avgFillPrice!.toLocaleString("en-US", { maximumFractionDigits: 4 })}` : "";
|
|
71
|
-
const limit = order.orderType === "MKT" ? "market" :
|
|
72
|
-
|
|
76
|
+
const limit = order.orderType === "MKT" ? "market" : order.orderType === "TRAIL" ? trailingText(order, qty).toLowerCase()
|
|
77
|
+
: `${order.orderType === "STP" ? "stop" : "limit"} ${(order.limitPrice ?? order.stopPrice)?.toLocaleString("en-US", { maximumFractionDigits: 4 }) ?? ""}`.trim();
|
|
78
|
+
// A bracket's exit says which one filled or ended.
|
|
79
|
+
const role = order.linkRole && order.linkRole !== "entry" ? ` ${linkRoleName[order.linkRole].toLowerCase()}` : "";
|
|
80
|
+
return `${order.action === "BUY" ? "Buy" : "Sell"} ${qty(order.quantity)} ${symbol}${role} ${limit}${filled}`;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
/**
|
|
@@ -145,7 +153,10 @@ class ProfileOrderFeed {
|
|
|
145
153
|
|
|
146
154
|
private observe(): void {
|
|
147
155
|
let state: string | undefined;
|
|
148
|
-
try { state = this.broker.getStatus?.(this.instance).state; } catch {
|
|
156
|
+
try { state = this.broker.getStatus?.(this.instance).state; } catch (error) {
|
|
157
|
+
logFailure("broker-trading.order-feed", "Read broker connection status failed")(error);
|
|
158
|
+
state = undefined;
|
|
159
|
+
}
|
|
149
160
|
const connected = state === "connected";
|
|
150
161
|
if (connected === this.connected) return;
|
|
151
162
|
this.connected = connected;
|
|
@@ -164,7 +175,10 @@ class ProfileOrderFeed {
|
|
|
164
175
|
this.publish();
|
|
165
176
|
},
|
|
166
177
|
});
|
|
167
|
-
} catch {
|
|
178
|
+
} catch (error) {
|
|
179
|
+
logFailure("broker-trading.order-feed", "Subscribe to broker orders failed")(error);
|
|
180
|
+
this.stopStream = undefined;
|
|
181
|
+
}
|
|
168
182
|
}
|
|
169
183
|
void this.read();
|
|
170
184
|
}
|
|
@@ -189,7 +203,10 @@ class ProfileOrderFeed {
|
|
|
189
203
|
}
|
|
190
204
|
|
|
191
205
|
private tradingOn(): boolean {
|
|
192
|
-
try { return this.broker.getTradingCapabilities?.(this.instance)?.enabled === true; } catch {
|
|
206
|
+
try { return this.broker.getTradingCapabilities?.(this.instance)?.enabled === true; } catch (error) {
|
|
207
|
+
logFailure("broker-trading.order-feed", "Read broker trading capabilities failed")(error);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
193
210
|
}
|
|
194
211
|
|
|
195
212
|
private async read(): Promise<void> {
|
|
@@ -211,7 +228,7 @@ class ProfileOrderFeed {
|
|
|
211
228
|
for (const [key, order] of missing) {
|
|
212
229
|
// A broker with no status read says nothing beyond its open list.
|
|
213
230
|
if (!this.broker.getOrderStatus) { this.orders.delete(key); continue; }
|
|
214
|
-
const status = await this.broker.getOrderStatus(this.instance, order.orderId).catch((
|
|
231
|
+
const status = await this.broker.getOrderStatus(this.instance, order.orderId).catch(logFailure("broker-trading.order-feed", "Read missing broker order status failed"));
|
|
215
232
|
if (generation !== this.generation) return;
|
|
216
233
|
if (status) this.apply(status, baseline ? "baseline" : "poll", false);
|
|
217
234
|
// Null is the broker unable to confirm it, not an end: keep it, unconfirmed, at its last report's time,
|
|
@@ -244,6 +261,9 @@ class ProfileOrderFeed {
|
|
|
244
261
|
if (publish) { this.prune(); this.publish(); }
|
|
245
262
|
// An order that started working needs the faster cadence without a stream.
|
|
246
263
|
if (!this.live && !brokerOrderDone(order.status) && (!previous || brokerOrderDone(previous.status))) this.schedule();
|
|
264
|
+
// One of a bracket or OCO pair ending moves the others (cancelled, or working once an entry fills): the broker's
|
|
265
|
+
// list says how, now rather than at the next read.
|
|
266
|
+
if (order.linkGroup && source !== "baseline" && source !== "poll" && brokerOrderDone(order.status) && !(previous && brokerOrderDone(previous.status))) this.refresh();
|
|
247
267
|
}
|
|
248
268
|
|
|
249
269
|
private prune(): void {
|
|
@@ -271,11 +291,23 @@ export class BrokerOrderFeed {
|
|
|
271
291
|
private readonly listeners = new Set<() => void>();
|
|
272
292
|
private readonly seen = new Map<string, Seen>();
|
|
273
293
|
private readonly fills = new Map<string, number>();
|
|
294
|
+
private readonly fillSyncs = new Map<string, { timer?: ReturnType<typeof setTimeout>; firstAt: number; running: boolean; again: boolean }>();
|
|
274
295
|
private notifier?: (notification: AppNotificationRequest) => void;
|
|
296
|
+
private syncPositions?: (instanceId: string) => Promise<unknown>;
|
|
275
297
|
|
|
276
298
|
/** Toasts go through the app's notifications once the plugin is set up. */
|
|
277
299
|
setNotifier(notify: ((notification: AppNotificationRequest) => void) | undefined): void { this.notifier = notify; }
|
|
278
300
|
|
|
301
|
+
/**
|
|
302
|
+
* How a fill reaches the portfolio: the app's sync of the profile's
|
|
303
|
+
* positions, which the plugin sets up. A fill or partial fill schedules it
|
|
304
|
+
* once for a burst of fills; one during a sync runs it again after.
|
|
305
|
+
*/
|
|
306
|
+
setPositionSync(sync: ((instanceId: string) => Promise<unknown>) | undefined): void {
|
|
307
|
+
this.syncPositions = sync;
|
|
308
|
+
if (!sync) this.cancelFillSyncs();
|
|
309
|
+
}
|
|
310
|
+
|
|
279
311
|
hold(broker: BrokerAdapter, instance: BrokerInstanceConfig): () => void {
|
|
280
312
|
let profile = this.profiles.get(instance.id);
|
|
281
313
|
if (!profile || profile.broker !== broker) {
|
|
@@ -298,6 +330,7 @@ export class BrokerOrderFeed {
|
|
|
298
330
|
this.profiles.clear();
|
|
299
331
|
this.seen.clear();
|
|
300
332
|
this.fills.clear();
|
|
333
|
+
this.cancelFillSyncs();
|
|
301
334
|
this.changed();
|
|
302
335
|
}
|
|
303
336
|
|
|
@@ -328,11 +361,46 @@ export class BrokerOrderFeed {
|
|
|
328
361
|
this.seen.delete(key);
|
|
329
362
|
this.seen.set(key, { filled, done: (before?.done ?? false) || brokerOrderDone(order.status) });
|
|
330
363
|
if (this.seen.size > SEEN_LIMIT) this.seen.delete(this.seen.keys().next().value!);
|
|
331
|
-
if (filled > (before?.filled ?? 0))
|
|
364
|
+
if (filled > (before?.filled ?? 0)) {
|
|
365
|
+
this.fills.set(instanceId, (this.fills.get(instanceId) ?? 0) + 1);
|
|
366
|
+
// A fill moves the account's positions. An order first seen already filled long ago (the first read at
|
|
367
|
+
// launch, which the startup import covers) does not.
|
|
368
|
+
if (before || Date.now() - order.updatedAt <= RECENT_END_MS) this.scheduleFillSync(instanceId);
|
|
369
|
+
}
|
|
332
370
|
if (quiet || !this.notifier) return;
|
|
333
371
|
const toast = orderChangeToast(before, order, Date.now());
|
|
334
372
|
if (toast) this.notifier({ ...toast, desktop: "when-inactive", body: orderToastBody(order) });
|
|
335
373
|
}
|
|
374
|
+
|
|
375
|
+
private scheduleFillSync(instanceId: string): void {
|
|
376
|
+
if (!this.syncPositions) return;
|
|
377
|
+
const now = Date.now();
|
|
378
|
+
const entry = this.fillSyncs.get(instanceId) ?? { firstAt: now, running: false, again: false };
|
|
379
|
+
if (entry.timer === undefined) entry.firstAt = now;
|
|
380
|
+
else clearTimeout(entry.timer);
|
|
381
|
+
entry.timer = setTimeout(() => this.runFillSync(instanceId), Math.max(0, Math.min(FILL_SYNC_MS, entry.firstAt + FILL_SYNC_MAX_WAIT_MS - now)));
|
|
382
|
+
this.fillSyncs.set(instanceId, entry);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private runFillSync(instanceId: string): void {
|
|
386
|
+
const entry = this.fillSyncs.get(instanceId);
|
|
387
|
+
const sync = this.syncPositions;
|
|
388
|
+
if (!entry || !sync) return;
|
|
389
|
+
entry.timer = undefined;
|
|
390
|
+
if (entry.running) { entry.again = true; return; }
|
|
391
|
+
entry.running = true;
|
|
392
|
+
// A failed sync leaves the portfolio as it was; the next fill, a manual sync or the next launch reads it again.
|
|
393
|
+
void Promise.resolve().then(() => sync(instanceId)).catch(logFailure("broker-trading.order-feed", "Sync positions after broker fill failed")).finally(() => {
|
|
394
|
+
entry.running = false;
|
|
395
|
+
if (this.fillSyncs.get(instanceId) !== entry) return;
|
|
396
|
+
if (entry.again) { entry.again = false; this.runFillSync(instanceId); } else if (entry.timer === undefined) this.fillSyncs.delete(instanceId);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private cancelFillSyncs(): void {
|
|
401
|
+
for (const entry of this.fillSyncs.values()) if (entry.timer !== undefined) clearTimeout(entry.timer);
|
|
402
|
+
this.fillSyncs.clear();
|
|
403
|
+
}
|
|
336
404
|
}
|
|
337
405
|
|
|
338
406
|
export const brokerOrderFeed = new BrokerOrderFeed();
|