gloomberb 0.16.2 → 0.16.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/package.json +4 -1
- package/src/api-client/askg.ts +43 -0
- package/src/api-client/auth.ts +15 -0
- package/src/api-client/beneficial-owners.ts +4 -0
- package/src/api-client/chat.ts +19 -0
- package/src/api-client/company-kpis.ts +14 -2
- package/src/api-client/data.ts +34 -16
- package/src/api-client/index.ts +12 -0
- package/src/api-client/normalizers.ts +27 -0
- package/src/api-client/paths.ts +88 -23
- package/src/api-client/request.ts +2 -1
- package/src/api-client/research-activity.ts +31 -0
- package/src/api-client/socket.ts +39 -8
- package/src/api-client/types.ts +57 -1
- package/src/api-client/upgrade-experiment.ts +90 -0
- package/src/app/global-shortcuts.ts +11 -1
- package/src/app/keybindings/actions.ts +10 -0
- package/src/app/pane-runtime/index.tsx +8 -0
- package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
- package/src/app/pane-runtime/plugin-bindings.ts +65 -25
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
- package/src/app/runtime/desktop-deeplink.ts +5 -0
- package/src/app/star-prompt/model.ts +98 -0
- package/src/app/star-prompt/runtime.ts +41 -0
- package/src/app/star-prompt/status-line.tsx +190 -0
- package/src/app.tsx +13 -7
- package/src/architecture/core-builtin-allowlist.json +70 -0
- package/src/brokers/account-cache.ts +45 -0
- package/src/brokers/signed-in/adapter.ts +33 -2
- package/src/brokers/signed-in/client.ts +10 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
- package/src/brokers/signed-in/sign-in.ts +62 -5
- package/src/capabilities/registry.ts +8 -1
- package/src/capabilities/types.ts +2 -0
- package/src/cli/command-options.ts +74 -0
- package/src/cli/commands/command-utils.ts +29 -0
- package/src/cli/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +524 -148
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +223 -42
- package/src/cli/commands/search.ts +29 -8
- package/src/cli/commands/system.ts +99 -19
- package/src/cli/commands/themes.ts +64 -0
- package/src/cli/commands/ticker.ts +200 -73
- package/src/cli/desktop-pane-shot.ts +100 -16
- package/src/cli/errors.ts +25 -2
- package/src/cli/freshness.ts +7 -4
- package/src/cli/fx-pair.ts +57 -0
- package/src/cli/help.ts +40 -7
- package/src/cli/history-rows.ts +187 -15
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +21 -13
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +55 -7
- package/src/cli/pane-functions/capabilities.ts +13 -2
- package/src/cli/pane-functions/catalog.ts +13 -2
- package/src/cli/pane-functions/data.ts +36 -5
- package/src/cli/pane-functions/dom.ts +89 -28
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +71 -24
- package/src/cli/pane-functions/headless.ts +168 -35
- package/src/cli/pane-functions/index.ts +12 -2
- package/src/cli/pane-functions/market-session.ts +133 -0
- package/src/cli/pane-functions/options.ts +15 -4
- package/src/cli/pane-functions/report-notices.ts +63 -0
- package/src/cli/pane-functions/report.ts +12 -1
- package/src/cli/pane-functions/resolver.ts +32 -8
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +109 -23
- package/src/cli/registry.ts +8 -4
- package/src/cli/report-tables.ts +460 -0
- package/src/cli/result.ts +63 -16
- package/src/cli/row-window.ts +76 -0
- package/src/cli/scroll-truncation.ts +34 -0
- package/src/components/chart/composite/composite-chart.tsx +43 -4
- package/src/components/chart/composite/legend.tsx +39 -6
- package/src/components/chart/composite/rasterizer.ts +64 -4
- package/src/components/chart/composite/series-cues.ts +71 -0
- package/src/components/chart/composite/text-renderer.ts +16 -8
- package/src/components/chart/composite/types.ts +3 -0
- package/src/components/command-bar/commands/direct/index.ts +9 -1
- package/src/components/command-bar/commands/plugin/items.ts +3 -1
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
- package/src/components/command-bar/pane-templates/workflow.ts +5 -0
- package/src/components/command-bar/routes/root/command-items.ts +2 -0
- package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
- package/src/components/command-bar/routes/root/results.ts +40 -10
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/root/search-providers.ts +97 -14
- package/src/components/command-bar/surface/index.tsx +18 -4
- package/src/components/command-bar/theme-picker.tsx +48 -13
- package/src/components/command-bar/workflow/ops.ts +5 -3
- package/src/components/data-table/export.ts +6 -51
- package/src/components/data-table/view.tsx +31 -10
- package/src/components/layout/floating-pane.tsx +6 -2
- package/src/components/layout/market-summary.ts +23 -9
- package/src/components/layout/pane/content.tsx +46 -29
- package/src/components/layout/pane/cycle-order.ts +2 -11
- package/src/components/layout/pane/header.tsx +2 -1
- package/src/components/layout/pane/title.ts +1 -1
- package/src/components/layout/presentation.ts +22 -0
- package/src/components/layout/shell/active-drag.ts +67 -35
- package/src/components/layout/shell/cursor-occlusion.ts +6 -5
- package/src/components/layout/shell/drag/index.ts +16 -7
- package/src/components/layout/shell/drag/live.ts +193 -0
- package/src/components/layout/shell/drag/overlays.tsx +179 -35
- package/src/components/layout/shell/drag/runtime.ts +62 -50
- package/src/components/layout/shell/drag/slide.ts +10 -0
- package/src/components/layout/shell/index.tsx +84 -57
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
- package/src/components/layout/shell/native/surfaces.ts +30 -14
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +461 -180
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/pane/new-layout.ts +116 -0
- package/src/components/layout/shell/shortcuts.ts +5 -2
- package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
- package/src/components/layout/shell/visible-layout.ts +44 -1
- package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
- package/src/components/layout/status-bar.tsx +4 -1
- package/src/components/markdown-text.tsx +9 -5
- package/src/components/metric-treemap/heat-surface.tsx +27 -8
- package/src/components/metric-treemap/heat-tiles.ts +48 -17
- package/src/components/onboarding/onboarding-frame.tsx +33 -10
- package/src/components/onboarding/onboarding-wizard.tsx +49 -40
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
- package/src/components/onboarding/pro-step-layout.ts +86 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
- package/src/components/ui/button.tsx +10 -0
- package/src/components/ui/data-table/index.tsx +38 -0
- package/src/components/ui/data-table/types.ts +8 -1
- package/src/components/ui/data-table/visible-range.ts +2 -0
- package/src/components/ui/icon.tsx +11 -2
- package/src/components/ui/index.ts +1 -1
- package/src/core/connection-health.ts +1 -1
- package/src/core/state/app/layout-reducer.ts +89 -3
- package/src/core/state/app/state.ts +8 -5
- package/src/core/state/app/types.ts +13 -2
- package/src/data/config/layout.ts +55 -21
- package/src/data/config/store/fold-panes.ts +149 -0
- package/src/data/config/store/migrations.ts +7 -0
- package/src/data/config/store/node.ts +27 -6
- package/src/data/config/store/normalize.ts +37 -3
- package/src/data/json-persistence.ts +17 -3
- package/src/data/json-storage.ts +2 -0
- package/src/data/memory-resource-store.ts +46 -0
- package/src/i18n/es.ts +14 -1
- package/src/i18n/ja.ts +14 -1
- package/src/i18n/ko.ts +14 -1
- package/src/i18n/zh-cn.ts +14 -1
- package/src/i18n/zh-tw.ts +14 -1
- package/src/layout/desks.ts +1 -2
- package/src/layout/pane-layout-move.ts +264 -0
- package/src/layout/pane-manager/hidden-panes.ts +160 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/market-data/coordinator/entries.ts +7 -1
- package/src/market-data/coordinator/quotes.ts +2 -2
- package/src/market-data/market/freshness.ts +156 -11
- package/src/market-data/market/status.ts +40 -0
- package/src/market-data/options-alternatives.ts +69 -0
- package/src/market-data/quotes/contributions.ts +19 -2
- package/src/market-data/quotes/freshness.ts +5 -0
- package/src/market-data/quotes/metadata.ts +16 -0
- package/src/market-data/quotes/screener-live-quotes.ts +56 -5
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +1 -0
- package/src/plugins/builtin/account-management/navigation.ts +1 -1
- package/src/plugins/builtin/account-management/pane.tsx +31 -4
- package/src/plugins/builtin/alerts/quotes.ts +3 -0
- package/src/plugins/builtin/analytics/pane-model.ts +10 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
- package/src/plugins/builtin/analytics/risk-model.ts +37 -8
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
- package/src/plugins/builtin/attention/pane.tsx +1 -1
- package/src/plugins/builtin/backtest/pane.tsx +1 -1
- package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/broker-manager/model.ts +8 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
- package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
- package/src/plugins/builtin/broker-manager/table.ts +5 -0
- package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
- package/src/plugins/builtin/catalysts/detail.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
- package/src/plugins/builtin/chat/channels.ts +34 -36
- package/src/plugins/builtin/chat/command-bar.ts +138 -0
- package/src/plugins/builtin/chat/composer-commands.ts +21 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
- package/src/plugins/builtin/chat/content/footer.tsx +5 -3
- package/src/plugins/builtin/chat/content/index.tsx +21 -1
- package/src/plugins/builtin/chat/content/mentions.ts +2 -1
- package/src/plugins/builtin/chat/content/user-map.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +48 -0
- package/src/plugins/builtin/chat/conversations.ts +256 -0
- package/src/plugins/builtin/chat/discord-command.ts +91 -0
- package/src/plugins/builtin/chat/edit-window.ts +12 -6
- package/src/plugins/builtin/chat/ghost-user.ts +14 -0
- package/src/plugins/builtin/chat/index.tsx +40 -2
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
- package/src/plugins/builtin/chat/message/header.tsx +24 -15
- package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
- package/src/plugins/builtin/chat/message/render-state.ts +3 -0
- package/src/plugins/builtin/chat/pane-requests.ts +60 -0
- package/src/plugins/builtin/chat/pane-state.ts +32 -2
- package/src/plugins/builtin/chat/pane.tsx +8 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
- package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
- package/src/plugins/builtin/cloud/askg/model.ts +129 -70
- package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
- package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
- package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
- package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
- package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
- package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
- package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
- package/src/plugins/builtin/cloud/plugin.tsx +36 -1
- package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
- package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
- package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
- package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
- package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
- package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
- package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
- package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
- package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
- package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
- package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
- package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
- package/src/plugins/builtin/comment-letters/client.ts +1 -3
- package/src/plugins/builtin/company-attention/pane.tsx +1 -1
- package/src/plugins/builtin/company-kpis/client.ts +23 -8
- package/src/plugins/builtin/company-kpis/headless.ts +3 -1
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
- package/src/plugins/builtin/composite-plugins.ts +107 -64
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
- package/src/plugins/builtin/credit-boards/index.tsx +21 -21
- package/src/plugins/builtin/credit-boards/model.ts +0 -1
- package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
- package/src/plugins/builtin/debt-maturities/client.ts +37 -9
- package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +28 -7
- package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +42 -17
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/index.tsx +68 -22
- package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
- package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
- package/src/plugins/builtin/econ-statistics/view.ts +17 -0
- package/src/plugins/builtin/executives/data.ts +17 -6
- package/src/plugins/builtin/executives/pane.tsx +16 -7
- package/src/plugins/builtin/filing-events/pane.tsx +22 -5
- package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
- package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
- package/src/plugins/builtin/gpu/headless.ts +9 -3
- package/src/plugins/builtin/gpu/model.ts +14 -5
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +3 -6
- package/src/plugins/builtin/holders/format.ts +31 -1
- package/src/plugins/builtin/holders/headless.ts +32 -6
- package/src/plugins/builtin/holders/pane.tsx +25 -13
- package/src/plugins/builtin/holders/report-header.ts +46 -5
- package/src/plugins/builtin/insider/index.tsx +4 -4
- package/src/plugins/builtin/iv-history/charts.tsx +21 -7
- package/src/plugins/builtin/iv-history/client.ts +12 -2
- package/src/plugins/builtin/iv-history/format.ts +33 -2
- package/src/plugins/builtin/iv-history/headless.ts +22 -10
- package/src/plugins/builtin/iv-history/index.tsx +3 -3
- package/src/plugins/builtin/iv-history/model.ts +61 -13
- package/src/plugins/builtin/iv-history/pane.tsx +11 -3
- package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
- package/src/plugins/builtin/jobs/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
- package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
- package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
- package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
- package/src/plugins/builtin/layout-manager/index.tsx +9 -1
- package/src/plugins/builtin/macro-day/client.ts +24 -0
- package/src/plugins/builtin/macro-day/headless.ts +11 -8
- package/src/plugins/builtin/macro-day/index.tsx +3 -0
- package/src/plugins/builtin/macro-day/model.ts +11 -6
- package/src/plugins/builtin/macro-day/pane.tsx +12 -8
- package/src/plugins/builtin/macro-day/release-table.ts +40 -0
- package/src/plugins/builtin/macro-day/releases.ts +62 -44
- package/src/plugins/builtin/market-heatmap/data.ts +25 -5
- package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
- package/src/plugins/builtin/market-heatmap/model.ts +94 -3
- package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
- package/src/plugins/builtin/market-movers/headless.ts +31 -4
- package/src/plugins/builtin/market-movers/index.tsx +8 -1
- package/src/plugins/builtin/market-movers/model.ts +71 -17
- package/src/plugins/builtin/market-movers/table.ts +50 -23
- package/src/plugins/builtin/members/pane.tsx +10 -0
- package/src/plugins/builtin/news/headless.ts +9 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
- package/src/plugins/builtin/openfda/client.ts +1 -1
- package/src/plugins/builtin/options/carry.ts +95 -0
- package/src/plugins/builtin/options/index.tsx +58 -5
- package/src/plugins/builtin/options/strike-window.ts +183 -0
- package/src/plugins/builtin/options/table.ts +55 -3
- package/src/plugins/builtin/options/types.ts +8 -1
- package/src/plugins/builtin/options/view.tsx +161 -12
- package/src/plugins/builtin/options-positioning/headless.ts +4 -2
- package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +73 -17
- package/src/plugins/builtin/options-scenario/headless.ts +71 -24
- package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
- package/src/plugins/builtin/options-scenario/index.tsx +9 -2
- package/src/plugins/builtin/options-scenario/model.ts +33 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
- package/src/plugins/builtin/pe-band/model.ts +2 -2
- package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
- package/src/plugins/builtin/perps/equity-row.tsx +8 -5
- package/src/plugins/builtin/perps/model.ts +16 -5
- package/src/plugins/builtin/perps/pane.tsx +1 -1
- package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
- package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
- package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
- package/src/plugins/builtin/plugin-module.ts +87 -4
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
- package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
- package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
- package/src/plugins/builtin/realized-vol/headless.ts +3 -0
- package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
- package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
- package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
- package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
- package/src/plugins/builtin/risk-factors/data.ts +23 -9
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/seasonality/pane.tsx +1 -1
- package/src/plugins/builtin/sec/index.tsx +5 -5
- package/src/plugins/builtin/sectors/client.ts +10 -2
- package/src/plugins/builtin/sectors/headless.ts +35 -6
- package/src/plugins/builtin/sectors/index.tsx +18 -1
- package/src/plugins/builtin/sectors/sector-model.ts +150 -41
- package/src/plugins/builtin/shared/command-tab.ts +51 -0
- package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
- package/src/plugins/builtin/shared/members-menu.tsx +260 -0
- package/src/plugins/builtin/shared/report-freshness.ts +50 -2
- package/src/plugins/builtin/shared/ticker-request.ts +25 -1
- package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
- package/src/plugins/builtin/shared/volatility/options.ts +1 -46
- package/src/plugins/builtin/short-interest/index.tsx +19 -4
- package/src/plugins/builtin/short-interest/surface.tsx +4 -4
- package/src/plugins/builtin/short-volume/index.tsx +1 -10
- package/src/plugins/builtin/short-volume/pane.tsx +1 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
- package/src/plugins/builtin/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/themes/pane.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
- package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
- package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
- package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
- package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
- package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
- package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
- package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
- package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
- package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
- package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
- package/src/plugins/builtin/vol-surface/client.ts +6 -2
- package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
- package/src/plugins/builtin/vol-surface/headless.ts +124 -20
- package/src/plugins/builtin/vol-surface/model.ts +23 -11
- package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
- package/src/plugins/builtin/vol-surface/tables.ts +124 -0
- package/src/plugins/builtin/world-indices/client.ts +22 -4
- package/src/plugins/builtin/world-indices/headless.ts +54 -27
- package/src/plugins/builtin/world-indices/model.ts +4 -2
- package/src/plugins/builtin/world-indices/table.tsx +13 -2
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
- package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
- package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -6
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
- package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
- package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
- package/src/plugins/builtin/world-venue-map/model.ts +16 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/builtin-editorial.ts +126 -0
- package/src/plugins/catalog-backend.ts +16 -9
- package/src/plugins/catalog-browser.ts +127 -63
- package/src/plugins/catalog-manifest.ts +73 -40
- package/src/plugins/catalog-ui.ts +26 -6
- package/src/plugins/extracted-plugins.ts +7 -1
- package/src/plugins/ownership.ts +150 -7
- package/src/plugins/plugin-functions.ts +54 -0
- package/src/plugins/plugin-off.ts +36 -0
- package/src/plugins/registry/host-actions.ts +16 -0
- package/src/plugins/registry/index.ts +84 -24
- package/src/plugins/registry/plugin-state.ts +25 -1
- package/src/plugins/runtime/context.tsx +29 -0
- package/src/react/input.ts +48 -1
- package/src/remote/app-host.tsx +5 -0
- package/src/remote/controller.ts +3 -53
- package/src/remote/matching.ts +69 -0
- package/src/remote/schema.ts +1 -1
- package/src/remote/semantic-tree.tsx +14 -0
- package/src/remote/types.ts +2 -0
- package/src/renderers/browser/app-services.ts +3 -1
- package/src/renderers/dom/data-table/dom.ts +31 -0
- package/src/renderers/dom/data-table/index.tsx +5 -0
- package/src/renderers/dom/desktop/icons.tsx +14 -0
- package/src/renderers/dom/desktop/query-bar.tsx +59 -32
- package/src/renderers/dom/host/box.tsx +113 -17
- package/src/renderers/dom/host/drag-shield.ts +35 -0
- package/src/renderers/dom/host/live-frame.ts +62 -0
- package/src/renderers/dom/host/mouse.ts +8 -1
- package/src/renderers/dom/host/scroll-box.tsx +6 -5
- package/src/renderers/dom/host/tabs.tsx +3 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +26 -5
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
- package/src/renderers/electrobun/shared/protocol.ts +12 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/host/native.ts +0 -10
- package/src/renderers/electrobun/view/host/window-move.ts +28 -0
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
- package/src/renderers/electrobun/view/ui-host.tsx +2 -5
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/renderers/opentui/ui-host.tsx +7 -1
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/batches.ts +3 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +13 -7
- package/src/sources/provider-router/financials.ts +106 -16
- package/src/sources/provider-router/history.ts +27 -11
- package/src/sources/provider-router/index.ts +1 -1
- package/src/sources/provider-router/primary.ts +24 -6
- package/src/sources/sec-edgar.ts +68 -8
- package/src/state/pane-scroll-registry.tsx +67 -35
- package/src/sync/core-contributors.ts +4 -3
- package/src/test-support/cli-context.ts +2 -1
- package/src/test-support/data-provider.ts +34 -0
- package/src/test-support/margin-account.ts +108 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/series-colors.ts +46 -0
- package/src/theme/themes.ts +149 -0
- package/src/tickers/search/index.ts +22 -2
- package/src/tickers/search/ranking.ts +45 -1
- package/src/tickers/search/types.ts +3 -1
- package/src/time-series/resolution.ts +15 -0
- package/src/time-series/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- package/src/types/financials.ts +32 -3
- package/src/types/headless.ts +11 -0
- package/src/types/instrument.ts +8 -0
- package/src/types/plugin.ts +49 -3
- package/src/ui/host.tsx +16 -0
- package/src/ui/index.tsx +10 -3
- package/src/ui/scroll-box-axis.ts +26 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/currency-units.ts +8 -3
- package/src/utils/depositary-receipt.ts +54 -10
- package/src/utils/display-number.ts +63 -0
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fundamentals.ts +17 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- package/src/utils/price-history-integrity.ts +19 -6
- package/src/utils/price-history.ts +39 -6
- package/src/utils/rpc-timeout-error.ts +6 -2
- package/src/utils/sec-eps-basis.ts +159 -30
- package/src/utils/sec.ts +38 -34
- package/src/utils/throttled-fetch.ts +51 -22
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { Box, useUiCapabilities } from "../../../ui";
|
|
3
3
|
import { useShortcut } from "../../../react/input";
|
|
4
|
+
import { useRemoteUiNode } from "../../../remote/semantic-tree";
|
|
4
5
|
import { colors } from "../../../theme/colors";
|
|
5
6
|
import {
|
|
6
7
|
EmptyState,
|
|
@@ -17,7 +18,9 @@ import {
|
|
|
17
18
|
type QueryBarFilter,
|
|
18
19
|
} from "../../../components";
|
|
19
20
|
import type { PaneProps } from "../../../types/plugin";
|
|
20
|
-
import { useFxRatesMap } from "../../../market-data/hooks";
|
|
21
|
+
import { useChartQueries, useFxRatesMap, useResolvedEntryValue } from "../../../market-data/hooks";
|
|
22
|
+
import { buildChartKey } from "../../../market-data/selectors";
|
|
23
|
+
import { clipPriceHistoryToRange } from "../../../time-series/history-window";
|
|
21
24
|
import { useLiveTickerFinancials, useLiveTickerFinancialsMap } from "../../../state/hooks/live-ticker-financials";
|
|
22
25
|
import { buildPortfolioFinancialsMap } from "../../../market-data/portfolio-financials";
|
|
23
26
|
import { convertCurrency, formatCurrency } from "../../../utils/format";
|
|
@@ -27,6 +30,7 @@ import {
|
|
|
27
30
|
getFocusedTickerSymbol,
|
|
28
31
|
useAppSelector,
|
|
29
32
|
usePaneInstance,
|
|
33
|
+
usePaneSettingValue,
|
|
30
34
|
usePaneStateValue,
|
|
31
35
|
usePaneAppConfig,
|
|
32
36
|
} from "../../../state/app/context";
|
|
@@ -35,6 +39,7 @@ import { getSharedRegistry } from "../../registry";
|
|
|
35
39
|
import { resolveTickerOpenTarget } from "../../../tickers/open-target";
|
|
36
40
|
import { calculatePortfolioSummaryTotals } from "../portfolio-list/metrics";
|
|
37
41
|
import { resolvePortfolioNetLiquidation } from "../portfolio-list/account-metrics";
|
|
42
|
+
import { resolvePortfolioCash } from "../portfolio-list/allocation";
|
|
38
43
|
import {
|
|
39
44
|
buildTrackedCurrencies,
|
|
40
45
|
getCollectionTickersFromConfig,
|
|
@@ -50,12 +55,20 @@ import {
|
|
|
50
55
|
} from "./model";
|
|
51
56
|
import {
|
|
52
57
|
DEFAULT_KELLY_DRAFTS,
|
|
53
|
-
|
|
58
|
+
KELLY_PANE_MODES,
|
|
59
|
+
type KellyPaneMode,
|
|
54
60
|
type KellySizerDraft,
|
|
55
61
|
type KellySizerModeDrafts,
|
|
56
|
-
type KellySizingMode,
|
|
57
62
|
type PredictionMarketKellyAssumptions,
|
|
58
63
|
} from "./types";
|
|
64
|
+
import {
|
|
65
|
+
clampKellyHistoryYears,
|
|
66
|
+
estimateKellyHistoryInputs,
|
|
67
|
+
KELLY_HISTORY_DEFAULT_YEARS,
|
|
68
|
+
KELLY_HISTORY_MIN_PERIODS,
|
|
69
|
+
kellyHistoryRange,
|
|
70
|
+
kellyHistoryRequest,
|
|
71
|
+
} from "./history";
|
|
59
72
|
import { KELLY_PANE_ID } from "./constants";
|
|
60
73
|
import {
|
|
61
74
|
buildKellyCurveXAxisLabels,
|
|
@@ -69,9 +82,10 @@ import type { StaticChartXMarker } from "../../../components/chart/static";
|
|
|
69
82
|
import {
|
|
70
83
|
KellyCurveSection,
|
|
71
84
|
KellySensitivitySection,
|
|
85
|
+
buildKellyHistoryItems,
|
|
72
86
|
buildKellyResultItems,
|
|
73
87
|
} from "./sections";
|
|
74
|
-
import { getPortfolioPositionValue, resolveActivePortfolioId } from "./portfolio";
|
|
88
|
+
import { getPortfolioPositionValue, resolveActivePortfolioId, resolveKellyBankroll } from "./portfolio";
|
|
75
89
|
import { useKellyCommonAssumptions } from "./state";
|
|
76
90
|
|
|
77
91
|
export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
@@ -94,7 +108,13 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
94
108
|
const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
|
|
95
109
|
const { nativePaneChrome } = useUiCapabilities();
|
|
96
110
|
|
|
97
|
-
|
|
111
|
+
// A pane opened from a KELLY report (fn, shot) sizes from the ticker's own history, as the report does.
|
|
112
|
+
const [reportYears] = usePaneSettingValue<number | null>("historyYears", null);
|
|
113
|
+
const [reportBankroll] = usePaneSettingValue<number | null>("bankroll", null);
|
|
114
|
+
const [mode, setMode] = usePaneStateValue<KellyPaneMode>("mode", reportYears != null ? "history" : "binary");
|
|
115
|
+
const [historyYears, setHistoryYears] = usePaneStateValue<number>("historyYears", clampKellyHistoryYears(reportYears ?? KELLY_HISTORY_DEFAULT_YEARS));
|
|
116
|
+
// History reads inputs off the ticker's monthly returns and sizes them as a binary bet.
|
|
117
|
+
const sizingMode = mode === "history" ? "binary" : mode;
|
|
98
118
|
// A narrow terminal bar has no room for the view switch beside the ticker,
|
|
99
119
|
// price and account (plus the Side and Portfolio filters when shown), so
|
|
100
120
|
// there `s` stays a footer hint.
|
|
@@ -104,7 +124,10 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
104
124
|
const [drafts, setDrafts] = usePaneStateValue<KellySizerModeDrafts>("drafts", cloneKellyDrafts());
|
|
105
125
|
const [showSensitivity, setShowSensitivity] = usePaneStateValue<boolean>("showSensitivity", false);
|
|
106
126
|
const [selectedPortfolioId, setSelectedPortfolioId] = usePaneStateValue<string | null>("portfolioId", null);
|
|
107
|
-
const [bankrollOverride, setBankrollOverride] = usePaneStateValue<number | null>(
|
|
127
|
+
const [bankrollOverride, setBankrollOverride] = usePaneStateValue<number | null>(
|
|
128
|
+
"bankrollOverride",
|
|
129
|
+
typeof reportBankroll === "number" && reportBankroll > 0 ? reportBankroll : null,
|
|
130
|
+
);
|
|
108
131
|
const [currentValueOverride, setCurrentValueOverride] = usePaneStateValue<number | null>("currentValueOverride", null);
|
|
109
132
|
const [selectedFieldIndex, setSelectedFieldIndex] = useState(0);
|
|
110
133
|
const [activeInputId, setActiveInputId] = useState<string | null>(null);
|
|
@@ -214,9 +237,11 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
214
237
|
? portfolioFinancials.get(requestedSymbol!) ?? null
|
|
215
238
|
: financials;
|
|
216
239
|
const { accountState } = usePortfolioAccountState(activePortfolio, { brokerAccounts, config });
|
|
240
|
+
// Cash entered by hand counts toward a manual portfolio's bankroll; a broker's is in its Net Liq.
|
|
241
|
+
const manualCash = accountState ? null : resolvePortfolioCash(activePortfolio, null);
|
|
217
242
|
const trackedCurrencies = useMemo(
|
|
218
|
-
() => buildTrackedCurrencies(portfolioTickers, portfolioFinancials, accountState, config.baseCurrency),
|
|
219
|
-
[accountState, config.baseCurrency, portfolioFinancials, portfolioTickers],
|
|
243
|
+
() => buildTrackedCurrencies(portfolioTickers, portfolioFinancials, accountState, config.baseCurrency, manualCash?.currency),
|
|
244
|
+
[accountState, config.baseCurrency, manualCash?.currency, portfolioFinancials, portfolioTickers],
|
|
220
245
|
);
|
|
221
246
|
const exchangeRates = useFxRatesMap(trackedCurrencies);
|
|
222
247
|
const portfolioSummary = useMemo(
|
|
@@ -237,9 +262,12 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
237
262
|
(value) => convertCurrency(value, accountState?.account.currency ?? "", config.baseCurrency, exchangeRates),
|
|
238
263
|
accountState?.snapshotBasis,
|
|
239
264
|
);
|
|
240
|
-
const sourceBankroll =
|
|
241
|
-
|
|
242
|
-
:
|
|
265
|
+
const sourceBankroll = resolveKellyBankroll({
|
|
266
|
+
netLiquidation,
|
|
267
|
+
brokerPortfolio: !!activePortfolio?.brokerInstanceId,
|
|
268
|
+
holdings: portfolioSummary.allocationHoldings ?? [],
|
|
269
|
+
cashValue: manualCash ? convertCurrency(manualCash.amount, manualCash.currency, config.baseCurrency, exchangeRates) : null,
|
|
270
|
+
});
|
|
243
271
|
const sourceCurrentValue = getPortfolioPositionValue({
|
|
244
272
|
ticker,
|
|
245
273
|
financials: positionFinancials,
|
|
@@ -250,26 +278,57 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
250
278
|
const bankroll = bankrollOverride ?? sourceBankroll;
|
|
251
279
|
const currentValue = currentValueOverride ?? sourceCurrentValue;
|
|
252
280
|
const price = positionFinancials?.quote?.price ?? null;
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
281
|
+
// The same daily closes, through the same request, as the KELLY report reads.
|
|
282
|
+
const historyRequest = useMemo(
|
|
283
|
+
() => mode === "history" && ticker
|
|
284
|
+
? kellyHistoryRequest({ symbol: ticker.metadata.ticker, exchange: ticker.metadata.exchange ?? "" }, historyYears)
|
|
285
|
+
: null,
|
|
286
|
+
[historyYears, mode, ticker?.metadata.exchange, ticker?.metadata.ticker],
|
|
287
|
+
);
|
|
288
|
+
const historyEntries = useChartQueries(historyRequest ? [historyRequest] : []);
|
|
289
|
+
const historyEntry = historyRequest ? historyEntries.get(buildChartKey(historyRequest)) ?? null : null;
|
|
290
|
+
const historyPoints = useResolvedEntryValue(historyEntry);
|
|
291
|
+
const historyInputs = useMemo(
|
|
292
|
+
() => historyPoints
|
|
293
|
+
? estimateKellyHistoryInputs(clipPriceHistoryToRange(historyPoints, kellyHistoryRange(historyYears)), historyYears)
|
|
294
|
+
: null,
|
|
295
|
+
[historyPoints, historyYears],
|
|
258
296
|
);
|
|
297
|
+
const rawActiveDraft = drafts[sizingMode] ?? DEFAULT_KELLY_DRAFTS[sizingMode];
|
|
298
|
+
const { commonAssumptions, updateCommon } = useKellyCommonAssumptions(rawActiveDraft);
|
|
299
|
+
const activeDraft = useMemo(() => {
|
|
300
|
+
const draft = applyKellyCommonAssumptions(rawActiveDraft, commonAssumptions);
|
|
301
|
+
if (mode !== "history") return draft;
|
|
302
|
+
// Until the history loads there is no edge to size.
|
|
303
|
+
return {
|
|
304
|
+
...draft,
|
|
305
|
+
winProbability: historyInputs?.winProbability ?? 0,
|
|
306
|
+
upsideReturn: historyInputs?.upsideReturn ?? 0,
|
|
307
|
+
downsideReturn: historyInputs?.downsideReturn ?? 0,
|
|
308
|
+
};
|
|
309
|
+
}, [commonAssumptions, historyInputs, mode, rawActiveDraft]);
|
|
259
310
|
|
|
260
311
|
const updateDraft = useCallback((patch: Partial<KellySizerDraft>) => {
|
|
261
312
|
setDrafts((current) => ({
|
|
262
313
|
...cloneKellyDrafts(current),
|
|
263
|
-
[
|
|
264
|
-
...(current[
|
|
314
|
+
[sizingMode]: {
|
|
315
|
+
...(current[sizingMode] ?? DEFAULT_KELLY_DRAFTS[sizingMode]),
|
|
265
316
|
...patch,
|
|
266
317
|
} as KellySizerDraft,
|
|
267
318
|
}));
|
|
268
|
-
}, [
|
|
319
|
+
}, [sizingMode, setDrafts]);
|
|
269
320
|
|
|
270
|
-
const fields = useMemo(
|
|
271
|
-
() =>
|
|
272
|
-
|
|
321
|
+
const fields = useMemo<GridField[]>(
|
|
322
|
+
() => mode === "history"
|
|
323
|
+
? [{
|
|
324
|
+
id: "history:years",
|
|
325
|
+
label: "Years",
|
|
326
|
+
value: historyYears,
|
|
327
|
+
valueText: String(historyYears),
|
|
328
|
+
onValue: (value) => setHistoryYears(clampKellyHistoryYears(value)),
|
|
329
|
+
}]
|
|
330
|
+
: buildModeFields({ mode: sizingMode, draft: activeDraft, updateDraft }),
|
|
331
|
+
[activeDraft, historyYears, mode, setHistoryYears, sizingMode, updateDraft],
|
|
273
332
|
);
|
|
274
333
|
const commonFields = useMemo(
|
|
275
334
|
() => buildCommonFields({ common: commonAssumptions, updateCommon }),
|
|
@@ -301,35 +360,35 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
301
360
|
const selectedField = gridFields[Math.min(selectedFieldIndex, Math.max(0, gridFields.length - 1))] ?? null;
|
|
302
361
|
const result = useMemo(
|
|
303
362
|
() => calculateKellySizing({
|
|
304
|
-
mode,
|
|
363
|
+
mode: sizingMode,
|
|
305
364
|
draft: activeDraft,
|
|
306
365
|
bankroll,
|
|
307
366
|
currentValue,
|
|
308
367
|
price,
|
|
309
368
|
}),
|
|
310
|
-
[activeDraft, bankroll, currentValue,
|
|
369
|
+
[activeDraft, bankroll, currentValue, price, sizingMode],
|
|
311
370
|
);
|
|
312
|
-
const sensitivity = useMemo(() => buildSensitivityGrid(
|
|
371
|
+
const sensitivity = useMemo(() => buildSensitivityGrid(sizingMode, activeDraft), [activeDraft, sizingMode]);
|
|
313
372
|
const curveMaxFraction = useMemo(
|
|
314
|
-
() => getKellyCurveMaxFraction(
|
|
373
|
+
() => getKellyCurveMaxFraction(sizingMode, activeDraft, [
|
|
315
374
|
result.currentFraction,
|
|
316
375
|
result.clippedFraction,
|
|
317
376
|
result.fullKellyFraction,
|
|
318
377
|
]),
|
|
319
|
-
[activeDraft,
|
|
378
|
+
[activeDraft, sizingMode, result.clippedFraction, result.currentFraction, result.fullKellyFraction],
|
|
320
379
|
);
|
|
321
380
|
const curvePoints = useMemo(
|
|
322
|
-
() => buildKellyCurvePoints(
|
|
323
|
-
[activeDraft, curveMaxFraction,
|
|
381
|
+
() => buildKellyCurvePoints(sizingMode, activeDraft, curveMaxFraction),
|
|
382
|
+
[activeDraft, curveMaxFraction, sizingMode],
|
|
324
383
|
);
|
|
325
384
|
const curveXAxisLabels = useMemo(() => buildKellyCurveXAxisLabels(curveMaxFraction), [curveMaxFraction]);
|
|
326
385
|
const currentGrowth = useMemo(
|
|
327
|
-
() => calculateExpectedLogGrowthAtFraction(
|
|
328
|
-
[activeDraft,
|
|
386
|
+
() => calculateExpectedLogGrowthAtFraction(sizingMode, activeDraft, result.currentFraction),
|
|
387
|
+
[activeDraft, sizingMode, result.currentFraction],
|
|
329
388
|
);
|
|
330
389
|
const targetGrowth = useMemo(
|
|
331
|
-
() => calculateExpectedLogGrowthAtFraction(
|
|
332
|
-
[activeDraft,
|
|
390
|
+
() => calculateExpectedLogGrowthAtFraction(sizingMode, activeDraft, result.clippedFraction),
|
|
391
|
+
[activeDraft, sizingMode, result.clippedFraction],
|
|
333
392
|
);
|
|
334
393
|
const curveMarkers = useMemo<StaticChartXMarker[]>(() => {
|
|
335
394
|
if (!Number.isFinite(curveMaxFraction) || curveMaxFraction <= 0) return [];
|
|
@@ -400,8 +459,21 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
400
459
|
}
|
|
401
460
|
}, { enabled: focused });
|
|
402
461
|
|
|
462
|
+
// A report or shot of the History tab is as of the last daily close it read, not when it ran.
|
|
463
|
+
useRemoteUiNode(mode === "history" && historyInputs ? {
|
|
464
|
+
role: "report-freshness",
|
|
465
|
+
label: "History freshness",
|
|
466
|
+
getMetadata: () => ({ asOf: historyInputs.end }),
|
|
467
|
+
} : null);
|
|
468
|
+
// History mode says what it is waiting for, or why it has no inputs, before any sizing warning.
|
|
469
|
+
const historyStatus = mode !== "history" || historyInputs ? null
|
|
470
|
+
: historyEntry?.error ? { text: historyEntry.error.message, tone: "negative" as const }
|
|
471
|
+
: historyPoints ? { text: `under ${KELLY_HISTORY_MIN_PERIODS} monthly returns`, tone: "warning" as const }
|
|
472
|
+
: { text: "loading history", tone: "muted" as const };
|
|
403
473
|
usePaneFooter(KELLY_PANE_ID, () => ({
|
|
404
|
-
info:
|
|
474
|
+
info: historyStatus
|
|
475
|
+
? [{ id: "history", parts: [historyStatus] }]
|
|
476
|
+
: bankroll <= 0 && ticker
|
|
405
477
|
// Every size reads 0% until there is a bankroll; say so where status lives.
|
|
406
478
|
? [{ id: "bankroll", parts: [{ text: "no bankroll", tone: "warning" as const }] }]
|
|
407
479
|
: result.warnings.length > 0
|
|
@@ -415,15 +487,15 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
415
487
|
{ id: "search", key: "/", label: "search", onPress: focusTickerSearch },
|
|
416
488
|
...(viewInBar ? [] : [{ id: "sensitivity", key: "s", label: showSensitivity ? "ensitivity off" : "ensitivity", onPress: toggleSensitivity }]),
|
|
417
489
|
],
|
|
418
|
-
}), [activeInputId, bankroll, editSelectedField, focusTickerSearch, result.clipReasons, result.warnings, selectedField?.id, selectedField?.label, showSensitivity, ticker, toggleSensitivity, viewInBar]);
|
|
490
|
+
}), [activeInputId, bankroll, editSelectedField, focusTickerSearch, historyStatus?.text, historyStatus?.tone, result.clipReasons, result.warnings, selectedField?.id, selectedField?.label, showSensitivity, ticker, toggleSensitivity, viewInBar]);
|
|
419
491
|
|
|
420
492
|
const portfolioTabs = useMemo(
|
|
421
493
|
() => config.portfolios.map((portfolio) => ({ label: portfolio.name, value: portfolio.id })),
|
|
422
494
|
[config.portfolios],
|
|
423
495
|
);
|
|
424
|
-
const modeTabs = useMemo(() =>
|
|
496
|
+
const modeTabs = useMemo(() => KELLY_PANE_MODES.map((entry) => ({ label: entry.label, value: entry.id })), []);
|
|
425
497
|
const selectMode = (nextMode: string) => {
|
|
426
|
-
setMode(nextMode as
|
|
498
|
+
setMode(nextMode as KellyPaneMode);
|
|
427
499
|
setSelectedFieldIndex(0);
|
|
428
500
|
activateInput(null);
|
|
429
501
|
};
|
|
@@ -436,14 +508,18 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
436
508
|
});
|
|
437
509
|
const gridColumns = fieldGridColumns(width);
|
|
438
510
|
const gridRows = fieldGridRows(gridFields, gridColumns);
|
|
439
|
-
const resultItems =
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
511
|
+
const resultItems = [
|
|
512
|
+
// History's inputs lead: the window and the monthly figures the sizes come from.
|
|
513
|
+
...(mode === "history" && historyInputs ? buildKellyHistoryItems(historyInputs) : []),
|
|
514
|
+
...buildKellyResultItems({
|
|
515
|
+
result,
|
|
516
|
+
baseCurrency: config.baseCurrency,
|
|
517
|
+
currentGrowth,
|
|
518
|
+
targetGrowth,
|
|
519
|
+
width,
|
|
520
|
+
columns: gridColumns,
|
|
521
|
+
}),
|
|
522
|
+
];
|
|
447
523
|
const resultRows = statGridRows(resultItems, width, gridColumns);
|
|
448
524
|
// Query bar, the mode strip when it is not in the title bar, inputs, results.
|
|
449
525
|
const bodyRows = Math.max(0, height - 1 - tabRows - gridRows - resultRows);
|
|
@@ -2,6 +2,7 @@ import type { AppConfig } from "../../../types/config";
|
|
|
2
2
|
import type { TickerFinancials } from "../../../types/financials";
|
|
3
3
|
import type { TickerRecord } from "../../../types/ticker";
|
|
4
4
|
import { convertCurrency } from "../../../utils/format";
|
|
5
|
+
import { allocationTotal, type AllocationHolding } from "../portfolio-list/allocation";
|
|
5
6
|
import { getCollectionTypeFromConfig } from "../portfolio-list/pane/data";
|
|
6
7
|
import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue } from "../portfolio-list/position-metrics";
|
|
7
8
|
|
|
@@ -41,6 +42,30 @@ export function getPortfolioPositionValue({
|
|
|
41
42
|
?? (metrics.positionCount > 0 ? Number.NaN : 0);
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* What the portfolio is worth to bet with: its equity. A broker account's is
|
|
47
|
+
* its Net Liq. Otherwise it is the priced holdings at net value plus the cash
|
|
48
|
+
* entered for the portfolio, the portfolio header's Total. A broker portfolio
|
|
49
|
+
* with neither has no known equity: its positions alone are its gross, which
|
|
50
|
+
* margin puts above the equity, so it gives no bankroll.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveKellyBankroll({
|
|
53
|
+
netLiquidation,
|
|
54
|
+
brokerPortfolio,
|
|
55
|
+
holdings,
|
|
56
|
+
cashValue,
|
|
57
|
+
}: {
|
|
58
|
+
netLiquidation: number | null;
|
|
59
|
+
brokerPortfolio: boolean;
|
|
60
|
+
holdings: readonly AllocationHolding[];
|
|
61
|
+
/** Cash entered by hand, in the bankroll currency; NaN when it cannot be converted. */
|
|
62
|
+
cashValue: number | null;
|
|
63
|
+
}): number {
|
|
64
|
+
if (netLiquidation != null && Number.isFinite(netLiquidation)) return netLiquidation;
|
|
65
|
+
if (brokerPortfolio && cashValue == null) return 0;
|
|
66
|
+
return allocationTotal(holdings, cashValue) ?? 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
44
69
|
export function resolveActivePortfolioId({
|
|
45
70
|
requestedPortfolioId,
|
|
46
71
|
activeCollectionId,
|
|
@@ -13,11 +13,25 @@ import { colors, priceColor } from "../../../theme/colors";
|
|
|
13
13
|
import { Box } from "../../../ui";
|
|
14
14
|
import { displayWidth, formatCurrency, formatNumber } from "../../../utils/format";
|
|
15
15
|
import type { KellySizingResult, SensitivityGrid } from "./types";
|
|
16
|
+
import type { KellyHistoryInputs } from "./history";
|
|
16
17
|
import { formatPct, formatSignedPct } from "./view";
|
|
17
18
|
|
|
18
19
|
/** The longest result label ("Full growth"), which sets the band's label column. */
|
|
19
20
|
const RESULT_LABEL_CHARS = 11;
|
|
20
21
|
|
|
22
|
+
/** The inputs History reads off the ticker's monthly returns, ahead of the sizes. */
|
|
23
|
+
export function buildKellyHistoryItems(inputs: KellyHistoryInputs): StatItem[] {
|
|
24
|
+
const payoff = inputs.downsideReturn < 0 ? inputs.upsideReturn / -inputs.downsideReturn : null;
|
|
25
|
+
return [
|
|
26
|
+
// The dates take a row of their own so a narrow pane never cuts them.
|
|
27
|
+
{ id: "window", label: "Window", value: `${inputs.start} to ${inputs.end}`, detail: `${inputs.periods} monthly returns`, wide: true },
|
|
28
|
+
{ id: "winRate", label: "Win rate", value: formatPct(inputs.winProbability, 1), detail: `${inputs.wins}/${inputs.periods}` },
|
|
29
|
+
{ id: "avgGain", label: "Avg gain", value: formatSignedPct(inputs.upsideReturn), color: priceColor(inputs.upsideReturn) },
|
|
30
|
+
{ id: "avgLoss", label: "Avg loss", value: formatSignedPct(inputs.downsideReturn), color: priceColor(inputs.downsideReturn) },
|
|
31
|
+
{ id: "payoff", label: "Payoff", value: payoff == null ? "—" : `${formatNumber(payoff, 2)}x` },
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
/**
|
|
22
36
|
* The sizing result as one band under the inputs, laid out in the FieldGrid's
|
|
23
37
|
* columns: the Kelly fractions, then the move in money, then risk, then log
|
|
@@ -105,7 +105,7 @@ export interface SensitivityGrid {
|
|
|
105
105
|
cells: SensitivityGridCell[][];
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
const KELLY_MODES: Array<{ id: KellySizingMode; label: string }> = [
|
|
109
109
|
{ id: "binary", label: "Binary" },
|
|
110
110
|
{ id: "scenario", label: "Scenario" },
|
|
111
111
|
{ id: "risk-budget", label: "Risk" },
|
|
@@ -113,6 +113,14 @@ export const KELLY_MODES: Array<{ id: KellySizingMode; label: string }> = [
|
|
|
113
113
|
{ id: "asymmetric", label: "Asym" },
|
|
114
114
|
];
|
|
115
115
|
|
|
116
|
+
/** The pane's tabs: the sizing modes, and History, which reads a binary bet off the ticker's monthly returns. */
|
|
117
|
+
export type KellyPaneMode = KellySizingMode | "history";
|
|
118
|
+
|
|
119
|
+
export const KELLY_PANE_MODES: Array<{ id: KellyPaneMode; label: string }> = [
|
|
120
|
+
...KELLY_MODES,
|
|
121
|
+
{ id: "history", label: "History" },
|
|
122
|
+
];
|
|
123
|
+
|
|
116
124
|
export const DEFAULT_KELLY_COMMON_ASSUMPTIONS: KellyCommonAssumptions = {
|
|
117
125
|
kellyFraction: 0.25,
|
|
118
126
|
maxNameFraction: 0.08,
|
|
@@ -27,24 +27,32 @@ import {
|
|
|
27
27
|
|
|
28
28
|
let dispatchRef: ((action: AppAction) => void) | null = null;
|
|
29
29
|
let getStateRef: (() => { layout: LayoutConfig; termWidth: number; termHeight: number; focusedPaneId: string | null }) | null = null;
|
|
30
|
+
let restoreHiddenRef: ((layout: LayoutConfig) => LayoutConfig) | null = null;
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* `getState().layout` is the layout on screen. `restoreHidden` puts the panes
|
|
34
|
+
* it leaves out back into an edit of it before the edit is saved.
|
|
35
|
+
*/
|
|
31
36
|
export function setLayoutManagerDispatch(
|
|
32
37
|
dispatch: (action: AppAction) => void,
|
|
33
38
|
getState: () => { layout: LayoutConfig; termWidth: number; termHeight: number; focusedPaneId: string | null },
|
|
39
|
+
restoreHidden: (layout: LayoutConfig) => LayoutConfig,
|
|
34
40
|
) {
|
|
35
41
|
dispatchRef = dispatch;
|
|
36
42
|
getStateRef = getState;
|
|
43
|
+
restoreHiddenRef = restoreHidden;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
function clearLayoutManagerDispatch() {
|
|
40
47
|
dispatchRef = null;
|
|
41
48
|
getStateRef = null;
|
|
49
|
+
restoreHiddenRef = null;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
function persistLayout(layout: LayoutConfig) {
|
|
45
53
|
if (!dispatchRef) return;
|
|
46
54
|
dispatchRef({ type: "PUSH_LAYOUT_HISTORY" });
|
|
47
|
-
dispatchRef({ type: "UPDATE_LAYOUT", layout });
|
|
55
|
+
dispatchRef({ type: "UPDATE_LAYOUT", layout: restoreHiddenRef ? restoreHiddenRef(layout) : layout });
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
function getFocusedPane(layout: LayoutConfig, focusedPaneId: string | null) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { apiClient, type CloudMacroReleaseDaysPayload } from "../../../api-client";
|
|
2
|
+
import { createPluginCache } from "../../../data/plugin-cache";
|
|
1
3
|
import { getSharedMarketDataCoordinator, MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
|
|
2
4
|
import type { InstrumentRef } from "../../../market-data/request-types";
|
|
3
5
|
import { tickerHasListingSuffix } from "../../../sources/listing-symbols";
|
|
@@ -6,6 +8,28 @@ import type { PricePoint } from "../../../types/financials";
|
|
|
6
8
|
import { abortable, abortError } from "../../../utils/async-deadline";
|
|
7
9
|
import { errorMessage } from "../../../utils/errors";
|
|
8
10
|
import { resolveExchangeTimeZone } from "../../../utils/exchanges";
|
|
11
|
+
import { bundledMacroReleases, mergeMacroReleases, type MacroReleaseList } from "./releases";
|
|
12
|
+
|
|
13
|
+
export const macroReleaseCache = createPluginCache<CloudMacroReleaseDaysPayload>({
|
|
14
|
+
kind: "macro-release-days", source: "gloom-cloud", schemaVersion: 1,
|
|
15
|
+
// A release lands at 8:30 or 14:00 ET; the last list read is kept for a terminal that goes offline.
|
|
16
|
+
policy: { staleMs: 30 * 60_000, expireMs: 90 * 86_400_000 },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The bundled list, extended by the server's when it reaches further. A
|
|
21
|
+
* failed read keeps the last list read; with none, the bundled list alone.
|
|
22
|
+
*/
|
|
23
|
+
export async function loadMacroReleases(options: { force?: boolean } = {},
|
|
24
|
+
read: () => Promise<CloudMacroReleaseDaysPayload> = () => apiClient.getCloudMacroReleaseDays()): Promise<MacroReleaseList> {
|
|
25
|
+
const floor = bundledMacroReleases();
|
|
26
|
+
try {
|
|
27
|
+
const result = await macroReleaseCache.load("release-days", read, { force: options.force });
|
|
28
|
+
return mergeMacroReleases(floor, result.data);
|
|
29
|
+
} catch {
|
|
30
|
+
return floor;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
9
33
|
|
|
10
34
|
export interface MacroDayHistory {
|
|
11
35
|
history: PricePoint[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
2
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
3
|
-
import { loadMacroDayHistory } from "./client";
|
|
3
|
+
import { loadMacroDayHistory, loadMacroReleases } from "./client";
|
|
4
4
|
import { projectMacroDays, type MacroDayStats } from "./model";
|
|
5
|
-
import { MACRO_EVENT_KINDS, MACRO_EVENT_LABELS,
|
|
5
|
+
import { MACRO_EVENT_KINDS, MACRO_EVENT_LABELS, MACRO_RELEASE_SOURCES, staleMacroReleasesNotice } from "./releases";
|
|
6
6
|
|
|
7
7
|
// A mean that rounds to zero reads 0.00%, never -0.00%.
|
|
8
8
|
const percent = (value: unknown) => typeof value !== "number" ? "--" : /[1-9]/.test((value * 100).toFixed(2)) ? `${(value * 100).toFixed(2)}%` : "0.00%";
|
|
@@ -11,12 +11,12 @@ const share = (value: unknown) => typeof value === "number" ? `${Math.round(valu
|
|
|
11
11
|
|
|
12
12
|
export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
13
13
|
shape: "bundle", argument: { kind: "ticker", description: "Ticker" },
|
|
14
|
-
// Dated by the
|
|
14
|
+
// Dated by the release list (releasesThrough) as much as by the closes, so no session schedule applies.
|
|
15
15
|
freshness: { status: "not-a-feed", basis: "release-day study on daily closes" },
|
|
16
16
|
describe: (args) => `MDAY ${args.symbols[0] ?? ""}`,
|
|
17
17
|
discovery: { screenshotReadiness: "live-dom", limitations: [
|
|
18
18
|
"US listings; close-to-close on the release day.",
|
|
19
|
-
|
|
19
|
+
"Published CPI, jobs and FOMC days; sessions after the list's last covered day (releasesThrough) are left out.",
|
|
20
20
|
] },
|
|
21
21
|
options: [
|
|
22
22
|
{ key: "release", type: "enum", values: [{ value: "all" }, ...MACRO_EVENT_KINDS.map((value) => ({ value }))], defaultValue: "all",
|
|
@@ -25,9 +25,11 @@ export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
25
25
|
],
|
|
26
26
|
async load(args, ctx) {
|
|
27
27
|
const instrument = await resolveHeadlessInstrument(ctx, args.symbols[0]!);
|
|
28
|
-
const history = await loadMacroDayHistory({ instrument, signal: ctx.signal }, ctx.marketData);
|
|
28
|
+
const [history, releases] = await Promise.all([loadMacroDayHistory({ instrument, signal: ctx.signal }, ctx.marketData), loadMacroReleases()]);
|
|
29
29
|
ctx.signal.throwIfAborted();
|
|
30
|
-
const model = projectMacroDays(history.history, { symbol: instrument.symbol, lookbackYears: Number(args.options.lookbackYears) || 5
|
|
30
|
+
const model = projectMacroDays(history.history, { symbol: instrument.symbol, lookbackYears: Number(args.options.lookbackYears) || 5,
|
|
31
|
+
releases: releases.releases, coveredThrough: releases.coveredThrough });
|
|
32
|
+
const releasesNotice = staleMacroReleasesNotice(releases);
|
|
31
33
|
const row = (label: string, stats: MacroDayStats) => ({ label, ...stats });
|
|
32
34
|
const release = String(args.options.release ?? "all");
|
|
33
35
|
const events = release === "all" ? model.events : model.events.filter((event) => event.kind === release);
|
|
@@ -42,11 +44,12 @@ export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
42
44
|
{ key: "session", header: "Session" }, { key: "move", header: "Move", format: percent }, { key: "multiple", header: "vs normal", format: multiple }],
|
|
43
45
|
rows: events.map((event) => ({ ...event, event: MACRO_EVENT_LABELS[event.kind] })) },
|
|
44
46
|
],
|
|
45
|
-
complete: !history.stale && !history.error && model.events.length > 0,
|
|
47
|
+
complete: !history.stale && !history.error && !releasesNotice && model.events.length > 0,
|
|
46
48
|
unavailableSymbols: model.events.length ? [] : [instrument.symbol],
|
|
47
49
|
errors: history.error ? [history.error] : [],
|
|
50
|
+
...(releasesNotice ? { notes: [releasesNotice] } : {}),
|
|
48
51
|
metadata: { unit: "decimal return, close to close on the release day", start: model.start, asOf: model.asOf,
|
|
49
|
-
releasesThrough:
|
|
52
|
+
releasesThrough: releases.coveredThrough, releaseSources: Object.values(MACRO_RELEASE_SOURCES),
|
|
50
53
|
stale: history.stale, fetchedAt: history.fetchedAt, methodology: "docs/research-data.md#macro-day-reaction" },
|
|
51
54
|
};
|
|
52
55
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
3
|
+
import { macroReleaseCache } from "./client";
|
|
3
4
|
import { macroDayHeadless } from "./headless";
|
|
4
5
|
import { LOOKBACK_OPTIONS, MacroDayPane } from "./pane";
|
|
5
6
|
|
|
@@ -18,4 +19,6 @@ export const macroDayModule: PluginModule = {
|
|
|
18
19
|
keywords: ["mday", "macro day", "cpi day", "fomc day", "jobs day", "nfp", "payrolls", "event study", "release reaction"],
|
|
19
20
|
shortcut: "MDAY", publicShare: true,
|
|
20
21
|
}), headless: macroDayHeadless }],
|
|
22
|
+
setup(ctx) { macroReleaseCache.attach(ctx.persistence); },
|
|
23
|
+
dispose() { macroReleaseCache.reset(); },
|
|
21
24
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PricePoint } from "../../../types/financials";
|
|
2
2
|
import { zonedDateTimeParts } from "../../../utils/zoned-date-time";
|
|
3
|
-
import { MACRO_EVENT_KINDS,
|
|
3
|
+
import { MACRO_EVENT_KINDS, type MacroEventKind, type MacroRelease } from "./releases";
|
|
4
4
|
|
|
5
5
|
const DAY_MS = 86_400_000;
|
|
6
6
|
/** Longest calendar gap between two closes that is still one session apart: Thursday to Monday over a Friday holiday. */
|
|
@@ -71,10 +71,11 @@ function stats(moves: number[], normalAbs: number | null): MacroDayStats {
|
|
|
71
71
|
export function projectMacroDays(history: readonly PricePoint[], options: {
|
|
72
72
|
symbol: string;
|
|
73
73
|
lookbackYears: number;
|
|
74
|
-
releases
|
|
75
|
-
|
|
74
|
+
releases: readonly MacroRelease[];
|
|
75
|
+
/** The release list's last covered day. */
|
|
76
|
+
coveredThrough: string;
|
|
76
77
|
}): MacroDayModel {
|
|
77
|
-
const coveredThrough = options
|
|
78
|
+
const { coveredThrough } = options;
|
|
78
79
|
const byDate = new Map<string, number>();
|
|
79
80
|
for (const point of history) {
|
|
80
81
|
const time = new Date(point.date).getTime();
|
|
@@ -90,7 +91,11 @@ export function projectMacroDays(history: readonly PricePoint[], options: {
|
|
|
90
91
|
// Sessions after the published lists end may be unlisted release days, so they are left out of both sides.
|
|
91
92
|
const end = last < coveredThrough ? last : coveredThrough;
|
|
92
93
|
const startYear = Number(end.slice(0, 4)) - Math.max(1, options.lookbackYears);
|
|
93
|
-
const
|
|
94
|
+
const nominalStart = `${startYear}${end.slice(4)}`;
|
|
95
|
+
// The history can begin after the nominal start (daily bars reach back five years from today, the list's end is older).
|
|
96
|
+
// The first bar has no close before it, so the window opens there: a release before it falls outside the stated start.
|
|
97
|
+
const firstSession = sessions[0]!;
|
|
98
|
+
const start = firstSession > nominalStart ? firstSession : nominalStart;
|
|
94
99
|
/** Session date -> close-to-close move from the session before, when the two are one session apart. */
|
|
95
100
|
const moves = new Map<string, number>();
|
|
96
101
|
for (let index = 1; index < sessions.length; index++) {
|
|
@@ -101,7 +106,7 @@ export function projectMacroDays(history: readonly PricePoint[], options: {
|
|
|
101
106
|
|
|
102
107
|
const reactionSessions = new Set<string>();
|
|
103
108
|
const found: Omit<MacroDayEvent, "multiple">[] = [];
|
|
104
|
-
for (const release of options.releases
|
|
109
|
+
for (const release of options.releases) {
|
|
105
110
|
if (release.date <= start || release.date > end || !isWeekday(release.date)) continue;
|
|
106
111
|
const at = sessions.findIndex((date) => date >= release.date);
|
|
107
112
|
if (at < 1) continue;
|
|
@@ -9,9 +9,9 @@ import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
|
9
9
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
10
10
|
import type { PaneProps } from "../../../types/plugin";
|
|
11
11
|
import { Box } from "../../../ui";
|
|
12
|
-
import { loadMacroDayHistory } from "./client";
|
|
12
|
+
import { loadMacroDayHistory, loadMacroReleases } from "./client";
|
|
13
13
|
import { projectMacroDays, type MacroDayEvent, type MacroDayModel } from "./model";
|
|
14
|
-
import { MACRO_EVENT_KINDS, MACRO_EVENT_LABELS, type MacroEventKind } from "./releases";
|
|
14
|
+
import { MACRO_EVENT_KINDS, MACRO_EVENT_LABELS, staleMacroReleasesNotice, type MacroEventKind } from "./releases";
|
|
15
15
|
|
|
16
16
|
export const LOOKBACK_OPTIONS = [{ value: "1", label: "1Y" }, { value: "2", label: "2Y" }, { value: "3", label: "3Y" }, { value: "5", label: "5Y" }];
|
|
17
17
|
const RELEASE_OPTIONS = [{ value: "all", label: "All" }, ...MACRO_EVENT_KINDS.map((kind) => ({ value: kind, label: MACRO_EVENT_LABELS[kind] }))];
|
|
@@ -64,22 +64,26 @@ export function MacroDayPane({ width, height, focused }: PaneProps) {
|
|
|
64
64
|
const loader = useCallback(async (force: boolean) => {
|
|
65
65
|
controller.current?.abort();
|
|
66
66
|
controller.current = new AbortController();
|
|
67
|
-
const snapshot = await
|
|
67
|
+
const [snapshot, releases] = await Promise.all([
|
|
68
|
+
loadMacroDayHistory({ instrument: instrument!, forceRefresh: force, signal: controller.current.signal }),
|
|
69
|
+
loadMacroReleases({ force }),
|
|
70
|
+
]);
|
|
68
71
|
if (!snapshot.history.length && snapshot.error) throw new Error(snapshot.error);
|
|
69
|
-
return snapshot;
|
|
72
|
+
return { ...snapshot, releases };
|
|
70
73
|
}, [instrumentKey]);
|
|
71
74
|
const history = useAsyncResource(instrument ? loader : null);
|
|
72
75
|
useEffect(() => () => controller.current?.abort(), [loader]);
|
|
73
76
|
useAutoRefresh(history.updatedAt, history.load);
|
|
74
77
|
usePaneRefreshKey(() => { void history.reload(); }, { focused });
|
|
75
78
|
|
|
76
|
-
const model = useMemo(() => history.data ? projectMacroDays(history.data.history, { symbol: symbol ?? "", lookbackYears: Number(lookback) || 5
|
|
77
|
-
[history.data, lookback, symbol]);
|
|
79
|
+
const model = useMemo(() => history.data ? projectMacroDays(history.data.history, { symbol: symbol ?? "", lookbackYears: Number(lookback) || 5,
|
|
80
|
+
releases: history.data.releases.releases, coveredThrough: history.data.releases.coveredThrough }) : null, [history.data, lookback, symbol]);
|
|
81
|
+
const releasesNotice = history.data ? staleMacroReleasesNotice(history.data.releases) : null;
|
|
78
82
|
const rows = useMemo(() => model ? model.events.filter((event) => release === "all" || event.kind === release) : [], [model, release]);
|
|
79
83
|
const stats = useMemo(() => model?.events.length ? figures(model, release) : [], [model, release]);
|
|
80
84
|
|
|
81
85
|
usePaneNoticeFooter({ registrationId: "macro-day-notices", focused,
|
|
82
|
-
notices: [...new Set([identityError, history.error, history.data?.error].filter((value): value is string => !!value))] });
|
|
86
|
+
notices: [...new Set([identityError, history.error, history.data?.error, releasesNotice].filter((value): value is string => !!value))] });
|
|
83
87
|
usePaneFooter("macro-day", () => ({ info: [
|
|
84
88
|
...(history.loading ? [{ id: "loading", parts: [{ text: "loading history", tone: "muted" as const }] }] : []),
|
|
85
89
|
...(history.data?.stale ? [{ id: "stale", parts: [{ text: "stale history", tone: "warning" as const }] }] : []),
|
|
@@ -102,7 +106,7 @@ export function MacroDayPane({ width, height, focused }: PaneProps) {
|
|
|
102
106
|
{ id: "release", label: "Release", value: release, options: RELEASE_OPTIONS, onChange: setRelease, inline: true },
|
|
103
107
|
{ id: "lookback", label: "Lookback", value: String(lookback), options: LOOKBACK_OPTIONS, onChange: setLookback },
|
|
104
108
|
]} />
|
|
105
|
-
{!symbol ? <EmptyState title="
|
|
109
|
+
{!symbol ? <EmptyState title="Select a ticker." /> : <PaneStatusBody subject="macro-day moves" loading={history.loading && !model}
|
|
106
110
|
error={!model ? history.error ?? identityError ?? null : null} empty={!!model && !model.events.length}>
|
|
107
111
|
{model ? <>
|
|
108
112
|
<StatGrid items={stats} width={width} />
|