gloomberb 0.16.3 → 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/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/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +359 -99
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +164 -26
- 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 +62 -30
- package/src/cli/desktop-pane-shot.ts +97 -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 +14 -1
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +9 -6
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +50 -6
- package/src/cli/pane-functions/capabilities.ts +8 -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 +21 -13
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +67 -22
- package/src/cli/pane-functions/headless.ts +5 -4
- package/src/cli/pane-functions/index.ts +3 -0
- 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 +4 -0
- package/src/cli/pane-functions/resolver.ts +4 -6
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +105 -23
- package/src/cli/registry.ts +2 -1
- package/src/cli/result.ts +50 -13
- 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/view.tsx +28 -9
- 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 +1 -1
- package/src/components/layout/shell/drag/index.ts +4 -0
- package/src/components/layout/shell/drag/live.ts +76 -5
- package/src/components/layout/shell/drag/runtime.ts +8 -8
- package/src/components/layout/shell/index.tsx +47 -6
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +51 -10
- 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/visible-layout.ts +44 -1
- 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/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 +118 -18
- 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/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/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/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/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-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/headless.ts +6 -1
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- 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 +109 -39
- 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/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/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/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
- 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/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/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 +4 -4
- 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/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
- 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/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +1 -1
- 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/dom/desktop/icons.tsx +14 -0
- package/src/renderers/dom/desktop/query-bar.tsx +59 -32
- package/src/renderers/dom/host/box.tsx +1 -1
- package/src/renderers/dom/host/drag-shield.ts +17 -6
- package/src/renderers/dom/host/live-frame.ts +10 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +13 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +11 -5
- package/src/sources/provider-router/financials.ts +42 -15
- 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 +23 -5
- package/src/sources/sec-edgar.ts +68 -8
- 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/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/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- 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 +8 -4
- package/src/ui/index.tsx +2 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- 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
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { HeadlessPaneDefinition, HeadlessPaneRow } from "../../../types/headless";
|
|
2
|
+
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
3
|
+
import { MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
|
|
4
|
+
import { parseHedgeAmount } from "../options-scenario/hedge";
|
|
5
|
+
import { clipPriceHistoryToRange } from "../../../time-series/history-window";
|
|
6
|
+
import { formatNumber } from "../../../utils/format";
|
|
7
|
+
import { buildBinaryOutcomes, solveKellyFraction } from "./model";
|
|
8
|
+
import {
|
|
9
|
+
clampKellyHistoryYears,
|
|
10
|
+
estimateKellyHistoryInputs,
|
|
11
|
+
KELLY_HISTORY_DEFAULT_YEARS,
|
|
12
|
+
KELLY_HISTORY_MAX_YEARS,
|
|
13
|
+
KELLY_HISTORY_MIN_PERIODS,
|
|
14
|
+
kellyHistoryRange,
|
|
15
|
+
kellyHistoryRequest,
|
|
16
|
+
type KellyHistoryInputs,
|
|
17
|
+
} from "./history";
|
|
18
|
+
import { formatPct, formatSignedPct } from "./view";
|
|
19
|
+
|
|
20
|
+
/** The fractions of full Kelly a report sizes, largest first. */
|
|
21
|
+
const KELLY_FRACTIONS = [
|
|
22
|
+
{ id: "full", label: "Full Kelly", fraction: 1 },
|
|
23
|
+
{ id: "half", label: "Half Kelly", fraction: 0.5 },
|
|
24
|
+
{ id: "quarter", label: "Quarter Kelly", fraction: 0.25 },
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
/** The inputs a ticker's monthly returns give, as the History tab shows them. */
|
|
28
|
+
function inputEntries(inputs: KellyHistoryInputs, edge: number) {
|
|
29
|
+
const payoff = inputs.downsideReturn < 0 ? inputs.upsideReturn / -inputs.downsideReturn : null;
|
|
30
|
+
return [
|
|
31
|
+
{ key: "window", label: "Window", value: { start: inputs.start, end: inputs.end, months: inputs.periods },
|
|
32
|
+
formatted: `${inputs.start} to ${inputs.end}, ${inputs.periods} monthly returns` },
|
|
33
|
+
{ key: "winRate", label: "Win rate", value: inputs.winProbability,
|
|
34
|
+
formatted: `${formatPct(inputs.winProbability)} (${inputs.wins} of ${inputs.periods} months up)` },
|
|
35
|
+
{ key: "averageGain", label: "Average gain", value: inputs.upsideReturn, formatted: `${formatSignedPct(inputs.upsideReturn)} a month` },
|
|
36
|
+
{ key: "averageLoss", label: "Average loss", value: inputs.downsideReturn, formatted: `${formatSignedPct(inputs.downsideReturn)} a month` },
|
|
37
|
+
{ key: "payoffRatio", label: "Payoff ratio", value: payoff, formatted: payoff == null ? "no down month" : `${formatNumber(payoff, 2)}x` },
|
|
38
|
+
{ key: "edge", label: "Edge", value: edge, formatted: `${formatSignedPct(edge)} a month` },
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const kellyHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
43
|
+
shape: "bundle",
|
|
44
|
+
argument: { kind: "ticker", description: "Ticker to size" },
|
|
45
|
+
freshness: { source: "Your inputs and Gloom Cloud", status: "not-a-feed", basis: "monthly returns from daily closes", cadence: "daily", oldest: null },
|
|
46
|
+
description: "Kelly sizing for a ticker from its own monthly returns: win rate, payoff ratio and edge over a lookback, then full, half and quarter Kelly as a share of a bankroll.",
|
|
47
|
+
describe: (args) => `Kelly sizing | ${args.symbols[0] ?? ""}`,
|
|
48
|
+
discovery: {
|
|
49
|
+
dataRequirements: ["Gloom Cloud daily closes over the lookback, and the latest quote for share counts"],
|
|
50
|
+
limitations: ["Win rate, gain and loss are the ticker's own non-overlapping 21-session returns; past months are not a forecast."],
|
|
51
|
+
},
|
|
52
|
+
options: [
|
|
53
|
+
{
|
|
54
|
+
key: "bankroll",
|
|
55
|
+
type: "string",
|
|
56
|
+
settingKey: "bankroll",
|
|
57
|
+
placeholder: "amount",
|
|
58
|
+
example: "--bankroll 250000",
|
|
59
|
+
description: "Bankroll in your base currency, such as 250000, 250k or 1.5m; sizes each Kelly fraction in money.",
|
|
60
|
+
normalize: (value) => parseHedgeAmount(value, "bankroll"),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
key: "years",
|
|
64
|
+
type: "integer",
|
|
65
|
+
settingKey: "historyYears",
|
|
66
|
+
minimum: 1,
|
|
67
|
+
maximum: KELLY_HISTORY_MAX_YEARS,
|
|
68
|
+
defaultValue: KELLY_HISTORY_DEFAULT_YEARS,
|
|
69
|
+
description: "Lookback in years for the monthly returns.",
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
async load(args, ctx) {
|
|
73
|
+
const { symbol, exchange = "" } = await resolveHeadlessInstrument(ctx, args.symbols[0]!);
|
|
74
|
+
const years = clampKellyHistoryYears(args.options.years);
|
|
75
|
+
const range = kellyHistoryRange(years);
|
|
76
|
+
// The quote and the one history window the lookback needs load together, through the
|
|
77
|
+
// coordinator every chart reads daily closes from (the History tab's request, cache included).
|
|
78
|
+
const [entry, quote] = await Promise.all([
|
|
79
|
+
new MarketDataCoordinator(ctx.marketData).loadChart(kellyHistoryRequest({ symbol, exchange }, years), { forceRefresh: ctx.refresh }),
|
|
80
|
+
ctx.marketData.getQuote(symbol, exchange, ctx.refresh ? { cacheMode: "refresh" } : undefined).catch(() => null),
|
|
81
|
+
]);
|
|
82
|
+
const history = clipPriceHistoryToRange(resolveEntryValue(entry) ?? [], range);
|
|
83
|
+
ctx.signal.throwIfAborted();
|
|
84
|
+
const inputs = estimateKellyHistoryInputs(history, years);
|
|
85
|
+
if (!inputs) {
|
|
86
|
+
return {
|
|
87
|
+
complete: false,
|
|
88
|
+
unavailableSymbols: [symbol],
|
|
89
|
+
errors: [`${symbol}: fewer than ${KELLY_HISTORY_MIN_PERIODS} monthly returns in the last ${years} year${years === 1 ? "" : "s"}.`],
|
|
90
|
+
sections: [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const solve = solveKellyFraction(buildBinaryOutcomes(inputs.winProbability, inputs.upsideReturn, inputs.downsideReturn));
|
|
94
|
+
const bankrollValue = args.options.bankroll;
|
|
95
|
+
const bankroll = typeof bankrollValue === "number" && bankrollValue > 0 ? bankrollValue : null;
|
|
96
|
+
const baseCurrency = ctx.config.baseCurrency || "USD";
|
|
97
|
+
const price = quote && quote.price > 0 && Number.isFinite(quote.price) ? quote.price : null;
|
|
98
|
+
// Shares only when the bankroll and the price are in one currency.
|
|
99
|
+
const sharePrice = price != null && quote?.currency === baseCurrency ? price : null;
|
|
100
|
+
const rows: HeadlessPaneRow[] = KELLY_FRACTIONS.map(({ id, label, fraction }) => {
|
|
101
|
+
const share = solve.fraction * fraction;
|
|
102
|
+
const position = bankroll == null ? null : share * bankroll;
|
|
103
|
+
return {
|
|
104
|
+
id,
|
|
105
|
+
kelly: label,
|
|
106
|
+
fraction: share,
|
|
107
|
+
position,
|
|
108
|
+
shares: position != null && sharePrice != null ? Math.floor(position / sharePrice) : null,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
const notes = [
|
|
112
|
+
...(solve.warning ? [solve.warning] : []),
|
|
113
|
+
...(solve.fraction === 0 ? ["No positive edge over this window: every Kelly fraction sizes nothing."] : []),
|
|
114
|
+
...(bankroll == null ? [`Add --bankroll <amount> to size each fraction in ${baseCurrency}.`] : []),
|
|
115
|
+
];
|
|
116
|
+
return {
|
|
117
|
+
complete: true,
|
|
118
|
+
freshness: { asOf: inputs.end },
|
|
119
|
+
notes,
|
|
120
|
+
sections: [
|
|
121
|
+
{ title: "Inputs", entries: inputEntries(inputs, solve.expectedReturn) },
|
|
122
|
+
{
|
|
123
|
+
title: bankroll == null ? "Sizing" : `Sizing on ${formatNumber(bankroll, 0)} ${baseCurrency}`,
|
|
124
|
+
columns: [
|
|
125
|
+
{ key: "kelly", header: "Kelly" },
|
|
126
|
+
{ key: "fraction", header: "% of bankroll", align: "right", format: (value) => typeof value === "number" ? formatPct(value, 2) : "--" },
|
|
127
|
+
...(bankroll == null ? [] : [
|
|
128
|
+
{ key: "position", header: `Position ${baseCurrency}`, align: "right" as const,
|
|
129
|
+
format: (value: unknown) => typeof value === "number" ? formatNumber(value, 0) : "--" },
|
|
130
|
+
...(sharePrice == null ? [] : [{ key: "shares", header: "Shares", align: "right" as const,
|
|
131
|
+
format: (value: unknown) => typeof value === "number" ? formatNumber(value, 0) : "--" }]),
|
|
132
|
+
]),
|
|
133
|
+
],
|
|
134
|
+
rows,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
metadata: {
|
|
138
|
+
symbol,
|
|
139
|
+
inputs,
|
|
140
|
+
expectedReturn: solve.expectedReturn,
|
|
141
|
+
expectedLogGrowth: solve.expectedLogGrowth,
|
|
142
|
+
bankroll,
|
|
143
|
+
currency: baseCurrency,
|
|
144
|
+
price: price == null ? null : { value: price, currency: quote?.currency ?? null, asOf: quote?.lastUpdated ?? null },
|
|
145
|
+
method: "Binary Kelly on non-overlapping 21-session returns: win rate, mean gain and mean loss.",
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { PricePoint } from "../../../types/financials";
|
|
2
|
+
import type { TimeRange } from "../../../time-series/range";
|
|
3
|
+
import type { ChartRequest, InstrumentRef } from "../../../market-data/request-types";
|
|
4
|
+
import { getPricePointTimestamp } from "../../../utils/price-history";
|
|
5
|
+
|
|
6
|
+
/** Sessions in one holding period: a month of trading days. */
|
|
7
|
+
export const KELLY_HISTORY_PERIOD_SESSIONS = 21;
|
|
8
|
+
export const KELLY_HISTORY_DEFAULT_YEARS = 5;
|
|
9
|
+
export const KELLY_HISTORY_MAX_YEARS = 5;
|
|
10
|
+
/** Fewer monthly returns than this say too little about a win rate to size on. */
|
|
11
|
+
export const KELLY_HISTORY_MIN_PERIODS = 6;
|
|
12
|
+
|
|
13
|
+
const YEAR_MS = 365.25 * 24 * 60 * 60_000;
|
|
14
|
+
|
|
15
|
+
/** The binary inputs a ticker's own monthly returns imply over a lookback window. */
|
|
16
|
+
export interface KellyHistoryInputs {
|
|
17
|
+
winProbability: number;
|
|
18
|
+
/** Mean return of the up months, as a fraction. */
|
|
19
|
+
upsideReturn: number;
|
|
20
|
+
/** Mean return of the down months, as a negative fraction (0 when none fell). */
|
|
21
|
+
downsideReturn: number;
|
|
22
|
+
/** Up months over all months. */
|
|
23
|
+
wins: number;
|
|
24
|
+
periods: number;
|
|
25
|
+
/** Close the first return starts from, and the last bar, as YYYY-MM-DD. */
|
|
26
|
+
start: string;
|
|
27
|
+
end: string;
|
|
28
|
+
years: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function clampKellyHistoryYears(value: unknown): number {
|
|
32
|
+
const years = Math.round(Number(value));
|
|
33
|
+
return Number.isFinite(years) ? Math.min(KELLY_HISTORY_MAX_YEARS, Math.max(1, years)) : KELLY_HISTORY_DEFAULT_YEARS;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The shortest history range that covers the lookback, so a one-year window never loads five. */
|
|
37
|
+
export function kellyHistoryRange(years: number): TimeRange {
|
|
38
|
+
return clampKellyHistoryYears(years) <= 1 ? "1Y" : "5Y";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The daily closes the History tab and the KELLY report both read. */
|
|
42
|
+
export function kellyHistoryRequest(instrument: InstrumentRef, years: number): ChartRequest {
|
|
43
|
+
return { instrument, bufferRange: kellyHistoryRange(years), granularity: "resolution", resolution: "1d" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const dateKey = (time: number) => new Date(time).toISOString().slice(0, 10);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Win rate, mean gain and mean loss of non-overlapping 21-session returns,
|
|
50
|
+
* counted back from the last daily close so the newest month is complete.
|
|
51
|
+
* Null when the window holds fewer than `KELLY_HISTORY_MIN_PERIODS` of them.
|
|
52
|
+
*/
|
|
53
|
+
export function estimateKellyHistoryInputs(points: readonly PricePoint[], yearsInput: number): KellyHistoryInputs | null {
|
|
54
|
+
const years = clampKellyHistoryYears(yearsInput);
|
|
55
|
+
const closes = points
|
|
56
|
+
.map((point) => ({ time: getPricePointTimestamp(point), close: point.close }))
|
|
57
|
+
.filter((point) => Number.isFinite(point.time) && Number.isFinite(point.close) && point.close > 0)
|
|
58
|
+
.sort((left, right) => left.time - right.time);
|
|
59
|
+
const last = closes.at(-1);
|
|
60
|
+
if (!last) return null;
|
|
61
|
+
const cutoff = last.time - years * YEAR_MS;
|
|
62
|
+
const window = closes.filter((point) => point.time >= cutoff);
|
|
63
|
+
const picks: typeof window = [];
|
|
64
|
+
for (let index = window.length - 1; index >= 0; index -= KELLY_HISTORY_PERIOD_SESSIONS) picks.unshift(window[index]!);
|
|
65
|
+
const returns = picks.slice(1).map((point, index) => point.close / picks[index]!.close - 1);
|
|
66
|
+
if (returns.length < KELLY_HISTORY_MIN_PERIODS) return null;
|
|
67
|
+
const gains = returns.filter((value) => value > 0);
|
|
68
|
+
const losses = returns.filter((value) => value < 0);
|
|
69
|
+
const mean = (values: number[]) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : 0;
|
|
70
|
+
return {
|
|
71
|
+
winProbability: gains.length / returns.length,
|
|
72
|
+
upsideReturn: mean(gains),
|
|
73
|
+
downsideReturn: mean(losses),
|
|
74
|
+
wins: gains.length,
|
|
75
|
+
periods: returns.length,
|
|
76
|
+
start: dateKey(picks[0]!.time),
|
|
77
|
+
end: dateKey(last.time),
|
|
78
|
+
years,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { PaneTemplateContext, PaneTemplateCreateOptions } from "../../../ty
|
|
|
2
2
|
import type { PluginModule } from "../plugin-module";
|
|
3
3
|
import { DEFAULT_FLOATING_SIZE, KELLY_PANE_ID } from "./constants";
|
|
4
4
|
import { KellySizerPane } from "./pane";
|
|
5
|
+
import { kellyHeadless } from "./headless";
|
|
5
6
|
|
|
6
7
|
function resolveTemplateSymbol(context: PaneTemplateContext, options?: PaneTemplateCreateOptions): string | null {
|
|
7
8
|
return options?.symbol
|
|
@@ -40,6 +41,7 @@ export const positionSizerModule: PluginModule = {
|
|
|
40
41
|
const symbol = resolveTemplateSymbol(context, options);
|
|
41
42
|
return symbol ? { params: { symbol }, placement: "floating" } : null;
|
|
42
43
|
},
|
|
44
|
+
headless: kellyHeadless,
|
|
43
45
|
},
|
|
44
46
|
],
|
|
45
47
|
};
|
|
@@ -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";
|
|
@@ -51,12 +55,20 @@ import {
|
|
|
51
55
|
} from "./model";
|
|
52
56
|
import {
|
|
53
57
|
DEFAULT_KELLY_DRAFTS,
|
|
54
|
-
|
|
58
|
+
KELLY_PANE_MODES,
|
|
59
|
+
type KellyPaneMode,
|
|
55
60
|
type KellySizerDraft,
|
|
56
61
|
type KellySizerModeDrafts,
|
|
57
|
-
type KellySizingMode,
|
|
58
62
|
type PredictionMarketKellyAssumptions,
|
|
59
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";
|
|
60
72
|
import { KELLY_PANE_ID } from "./constants";
|
|
61
73
|
import {
|
|
62
74
|
buildKellyCurveXAxisLabels,
|
|
@@ -70,6 +82,7 @@ import type { StaticChartXMarker } from "../../../components/chart/static";
|
|
|
70
82
|
import {
|
|
71
83
|
KellyCurveSection,
|
|
72
84
|
KellySensitivitySection,
|
|
85
|
+
buildKellyHistoryItems,
|
|
73
86
|
buildKellyResultItems,
|
|
74
87
|
} from "./sections";
|
|
75
88
|
import { getPortfolioPositionValue, resolveActivePortfolioId, resolveKellyBankroll } from "./portfolio";
|
|
@@ -95,7 +108,13 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
95
108
|
const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
|
|
96
109
|
const { nativePaneChrome } = useUiCapabilities();
|
|
97
110
|
|
|
98
|
-
|
|
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;
|
|
99
118
|
// A narrow terminal bar has no room for the view switch beside the ticker,
|
|
100
119
|
// price and account (plus the Side and Portfolio filters when shown), so
|
|
101
120
|
// there `s` stays a footer hint.
|
|
@@ -105,7 +124,10 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
105
124
|
const [drafts, setDrafts] = usePaneStateValue<KellySizerModeDrafts>("drafts", cloneKellyDrafts());
|
|
106
125
|
const [showSensitivity, setShowSensitivity] = usePaneStateValue<boolean>("showSensitivity", false);
|
|
107
126
|
const [selectedPortfolioId, setSelectedPortfolioId] = usePaneStateValue<string | null>("portfolioId", null);
|
|
108
|
-
const [bankrollOverride, setBankrollOverride] = usePaneStateValue<number | null>(
|
|
127
|
+
const [bankrollOverride, setBankrollOverride] = usePaneStateValue<number | null>(
|
|
128
|
+
"bankrollOverride",
|
|
129
|
+
typeof reportBankroll === "number" && reportBankroll > 0 ? reportBankroll : null,
|
|
130
|
+
);
|
|
109
131
|
const [currentValueOverride, setCurrentValueOverride] = usePaneStateValue<number | null>("currentValueOverride", null);
|
|
110
132
|
const [selectedFieldIndex, setSelectedFieldIndex] = useState(0);
|
|
111
133
|
const [activeInputId, setActiveInputId] = useState<string | null>(null);
|
|
@@ -256,26 +278,57 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
256
278
|
const bankroll = bankrollOverride ?? sourceBankroll;
|
|
257
279
|
const currentValue = currentValueOverride ?? sourceCurrentValue;
|
|
258
280
|
const price = positionFinancials?.quote?.price ?? null;
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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],
|
|
264
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]);
|
|
265
310
|
|
|
266
311
|
const updateDraft = useCallback((patch: Partial<KellySizerDraft>) => {
|
|
267
312
|
setDrafts((current) => ({
|
|
268
313
|
...cloneKellyDrafts(current),
|
|
269
|
-
[
|
|
270
|
-
...(current[
|
|
314
|
+
[sizingMode]: {
|
|
315
|
+
...(current[sizingMode] ?? DEFAULT_KELLY_DRAFTS[sizingMode]),
|
|
271
316
|
...patch,
|
|
272
317
|
} as KellySizerDraft,
|
|
273
318
|
}));
|
|
274
|
-
}, [
|
|
319
|
+
}, [sizingMode, setDrafts]);
|
|
275
320
|
|
|
276
|
-
const fields = useMemo(
|
|
277
|
-
() =>
|
|
278
|
-
|
|
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],
|
|
279
332
|
);
|
|
280
333
|
const commonFields = useMemo(
|
|
281
334
|
() => buildCommonFields({ common: commonAssumptions, updateCommon }),
|
|
@@ -307,35 +360,35 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
307
360
|
const selectedField = gridFields[Math.min(selectedFieldIndex, Math.max(0, gridFields.length - 1))] ?? null;
|
|
308
361
|
const result = useMemo(
|
|
309
362
|
() => calculateKellySizing({
|
|
310
|
-
mode,
|
|
363
|
+
mode: sizingMode,
|
|
311
364
|
draft: activeDraft,
|
|
312
365
|
bankroll,
|
|
313
366
|
currentValue,
|
|
314
367
|
price,
|
|
315
368
|
}),
|
|
316
|
-
[activeDraft, bankroll, currentValue,
|
|
369
|
+
[activeDraft, bankroll, currentValue, price, sizingMode],
|
|
317
370
|
);
|
|
318
|
-
const sensitivity = useMemo(() => buildSensitivityGrid(
|
|
371
|
+
const sensitivity = useMemo(() => buildSensitivityGrid(sizingMode, activeDraft), [activeDraft, sizingMode]);
|
|
319
372
|
const curveMaxFraction = useMemo(
|
|
320
|
-
() => getKellyCurveMaxFraction(
|
|
373
|
+
() => getKellyCurveMaxFraction(sizingMode, activeDraft, [
|
|
321
374
|
result.currentFraction,
|
|
322
375
|
result.clippedFraction,
|
|
323
376
|
result.fullKellyFraction,
|
|
324
377
|
]),
|
|
325
|
-
[activeDraft,
|
|
378
|
+
[activeDraft, sizingMode, result.clippedFraction, result.currentFraction, result.fullKellyFraction],
|
|
326
379
|
);
|
|
327
380
|
const curvePoints = useMemo(
|
|
328
|
-
() => buildKellyCurvePoints(
|
|
329
|
-
[activeDraft, curveMaxFraction,
|
|
381
|
+
() => buildKellyCurvePoints(sizingMode, activeDraft, curveMaxFraction),
|
|
382
|
+
[activeDraft, curveMaxFraction, sizingMode],
|
|
330
383
|
);
|
|
331
384
|
const curveXAxisLabels = useMemo(() => buildKellyCurveXAxisLabels(curveMaxFraction), [curveMaxFraction]);
|
|
332
385
|
const currentGrowth = useMemo(
|
|
333
|
-
() => calculateExpectedLogGrowthAtFraction(
|
|
334
|
-
[activeDraft,
|
|
386
|
+
() => calculateExpectedLogGrowthAtFraction(sizingMode, activeDraft, result.currentFraction),
|
|
387
|
+
[activeDraft, sizingMode, result.currentFraction],
|
|
335
388
|
);
|
|
336
389
|
const targetGrowth = useMemo(
|
|
337
|
-
() => calculateExpectedLogGrowthAtFraction(
|
|
338
|
-
[activeDraft,
|
|
390
|
+
() => calculateExpectedLogGrowthAtFraction(sizingMode, activeDraft, result.clippedFraction),
|
|
391
|
+
[activeDraft, sizingMode, result.clippedFraction],
|
|
339
392
|
);
|
|
340
393
|
const curveMarkers = useMemo<StaticChartXMarker[]>(() => {
|
|
341
394
|
if (!Number.isFinite(curveMaxFraction) || curveMaxFraction <= 0) return [];
|
|
@@ -406,8 +459,21 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
406
459
|
}
|
|
407
460
|
}, { enabled: focused });
|
|
408
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 };
|
|
409
473
|
usePaneFooter(KELLY_PANE_ID, () => ({
|
|
410
|
-
info:
|
|
474
|
+
info: historyStatus
|
|
475
|
+
? [{ id: "history", parts: [historyStatus] }]
|
|
476
|
+
: bankroll <= 0 && ticker
|
|
411
477
|
// Every size reads 0% until there is a bankroll; say so where status lives.
|
|
412
478
|
? [{ id: "bankroll", parts: [{ text: "no bankroll", tone: "warning" as const }] }]
|
|
413
479
|
: result.warnings.length > 0
|
|
@@ -421,15 +487,15 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
421
487
|
{ id: "search", key: "/", label: "search", onPress: focusTickerSearch },
|
|
422
488
|
...(viewInBar ? [] : [{ id: "sensitivity", key: "s", label: showSensitivity ? "ensitivity off" : "ensitivity", onPress: toggleSensitivity }]),
|
|
423
489
|
],
|
|
424
|
-
}), [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]);
|
|
425
491
|
|
|
426
492
|
const portfolioTabs = useMemo(
|
|
427
493
|
() => config.portfolios.map((portfolio) => ({ label: portfolio.name, value: portfolio.id })),
|
|
428
494
|
[config.portfolios],
|
|
429
495
|
);
|
|
430
|
-
const modeTabs = useMemo(() =>
|
|
496
|
+
const modeTabs = useMemo(() => KELLY_PANE_MODES.map((entry) => ({ label: entry.label, value: entry.id })), []);
|
|
431
497
|
const selectMode = (nextMode: string) => {
|
|
432
|
-
setMode(nextMode as
|
|
498
|
+
setMode(nextMode as KellyPaneMode);
|
|
433
499
|
setSelectedFieldIndex(0);
|
|
434
500
|
activateInput(null);
|
|
435
501
|
};
|
|
@@ -442,14 +508,18 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
|
|
|
442
508
|
});
|
|
443
509
|
const gridColumns = fieldGridColumns(width);
|
|
444
510
|
const gridRows = fieldGridRows(gridFields, gridColumns);
|
|
445
|
-
const resultItems =
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
+
];
|
|
453
523
|
const resultRows = statGridRows(resultItems, width, gridColumns);
|
|
454
524
|
// Query bar, the mode strip when it is not in the title bar, inputs, results.
|
|
455
525
|
const bodyRows = Math.max(0, height - 1 - tabRows - gridRows - resultRows);
|
|
@@ -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[];
|