gloomberb 0.16.4 → 0.16.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -91
- package/package.json +4 -1
- package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
- package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
- package/src/account/billing.ts +57 -0
- package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
- package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
- package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
- package/src/account/session.ts +16 -0
- package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
- package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
- package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
- package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
- package/src/api-client/data.ts +24 -2
- package/src/api-client/earnings.ts +6 -0
- package/src/api-client/futures-curve.ts +56 -0
- package/src/api-client/index.ts +4 -1
- package/src/api-client/market-discovery.ts +10 -1
- package/src/api-client/paths.ts +7 -0
- package/src/api-client/research-activity.ts +15 -10
- package/src/api-client/types.ts +17 -1
- package/src/api-client/yield-curves.ts +8 -0
- package/src/app/global-shortcuts.ts +15 -1
- package/src/app/pane-runtime/ticker-open-runtime.ts +7 -3
- package/src/architecture/core-builtin-allowlist.json +4 -13
- package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
- package/src/capabilities/chart-series.ts +11 -1
- package/src/cli/commands/crypto-hints.ts +7 -0
- package/src/cli/commands/fred-series.ts +61 -0
- package/src/cli/commands/market.ts +407 -104
- package/src/cli/commands/news-hints.ts +62 -0
- package/src/cli/commands/news-search.ts +53 -0
- package/src/cli/commands/options-leaps.ts +5 -2
- package/src/cli/commands/overview.ts +93 -20
- package/src/cli/commands/remote.ts +2 -1
- package/src/cli/commands/search.ts +82 -9
- package/src/cli/commands/system.ts +55 -8
- package/src/cli/commands/ticker.ts +88 -11
- package/src/cli/context.ts +45 -14
- package/src/cli/desktop-pane-shot.ts +59 -7
- package/src/cli/earnings-calendar.ts +71 -0
- package/src/cli/freshness.ts +18 -7
- package/src/cli/glossary.ts +429 -0
- package/src/cli/history-rows.ts +48 -7
- package/src/cli/index-roots.ts +7 -0
- package/src/cli/index.ts +21 -7
- package/src/cli/known-gaps.ts +27 -0
- package/src/cli/listing-arg.ts +417 -56
- package/src/cli/not-a-ticker.ts +12 -5
- package/src/cli/pane-functions/access-gate.ts +5 -0
- package/src/cli/pane-functions/capabilities.ts +3 -2
- package/src/cli/pane-functions/catalog.ts +35 -5
- package/src/cli/pane-functions/data.ts +12 -8
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.ts +20 -6
- package/src/cli/pane-functions/freshness.ts +363 -43
- package/src/cli/pane-functions/function-help.ts +12 -5
- package/src/cli/pane-functions/headless.ts +45 -17
- package/src/cli/pane-functions/index.ts +63 -10
- package/src/cli/pane-functions/market-session.ts +22 -8
- package/src/cli/pane-functions/options.ts +63 -22
- package/src/cli/pane-functions/report-notices.ts +25 -7
- package/src/cli/pane-functions/report.ts +15 -8
- package/src/cli/pane-functions/resolver.ts +49 -12
- package/src/cli/pane-functions/screenshot.ts +110 -23
- package/src/cli/report-tables.ts +3 -0
- package/src/cli/result.ts +4 -1
- package/src/cli/staged-tickers.ts +52 -0
- package/src/cli/symbol-args.ts +9 -0
- package/src/cli/symbol-list.ts +172 -0
- package/src/cli/ticker-resolution.ts +223 -30
- package/src/cli/types.ts +5 -0
- package/src/components/chart/composite/axis-overlays.tsx +6 -2
- package/src/components/chart/composite/composite-chart.tsx +40 -6
- package/src/components/chart/composite/legend.tsx +49 -2
- package/src/components/chart/composite/panel-surface.tsx +44 -17
- package/src/components/chart/composite/price-axis-labels.tsx +3 -1
- package/src/components/command-bar/panel/index.tsx +11 -3
- package/src/components/command-bar/panel/runtime.ts +3 -0
- package/src/components/command-bar/panel/types.ts +2 -0
- package/src/components/command-bar/routes/root/provider-search.ts +4 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
- package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
- package/src/components/command-bar/workflow/tickers.ts +23 -4
- package/src/components/layout/detached-pane-shell.tsx +9 -0
- package/src/components/layout/floating-pane.tsx +3 -0
- package/src/components/layout/header.tsx +20 -5
- package/src/components/layout/pane/content.tsx +7 -13
- package/src/components/layout/pane/footer/index.tsx +6 -2
- package/src/components/layout/pane/footer/model.ts +35 -1
- package/src/components/layout/pane/header.tsx +24 -1
- package/src/components/layout/pane/index.tsx +12 -2
- package/src/components/layout/pane/sidebar.tsx +78 -10
- package/src/components/layout/shell/active-drag.ts +21 -5
- package/src/components/layout/shell/drag/index.ts +39 -9
- package/src/components/layout/shell/drag/live.ts +61 -51
- package/src/components/layout/shell/drag/runtime.ts +54 -12
- package/src/components/layout/shell/index.tsx +25 -7
- package/src/components/layout/shell/menu.ts +5 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
- package/src/components/layout/shell/pane/layers.tsx +192 -45
- package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
- package/src/components/markdown-text.tsx +62 -20
- package/src/components/metric-treemap/heat-surface.tsx +10 -1
- package/src/components/onboarding/account-step/form-panel.tsx +1 -1
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-keyboard.ts +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +2 -2
- package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
- package/src/components/onboarding/wizard-account.ts +2 -2
- package/src/components/pane-template-wizard.tsx +9 -1
- package/src/components/price-selector-dialog.tsx +11 -2
- package/src/components/sign-in-gate.tsx +3 -3
- package/src/components/ticker/list-table-view.tsx +24 -1
- package/src/components/ui/action-row.tsx +2 -1
- package/src/components/ui/button.tsx +7 -1
- package/src/components/ui/choice-dialog.tsx +6 -4
- package/src/components/ui/confirm-dialog.tsx +28 -18
- package/src/components/ui/data-table/index.tsx +17 -6
- package/src/components/ui/data-table/opentui/index.tsx +37 -6
- package/src/components/ui/data-table/types.ts +12 -0
- package/src/components/ui/dialog-actions.tsx +64 -0
- package/src/components/ui/display.tsx +17 -3
- package/src/components/ui/frame.tsx +35 -2
- package/src/components/ui/icon.tsx +2 -0
- package/src/components/ui/text-prompt-dialog.tsx +11 -9
- package/src/data/cached-query.ts +2 -0
- package/src/data/config/store/normalize.ts +7 -2
- package/src/data/ticker-repository.ts +5 -0
- package/src/data/ticker-store.ts +15 -0
- package/src/docs/content.ts +215 -0
- package/src/docs/load.ts +139 -0
- package/src/docs/requests.ts +39 -0
- package/src/docs/snapshot.chunk.ts +8 -0
- package/src/docs/snapshot.json +7416 -0
- package/src/docs/topics.ts +216 -0
- package/src/i18n/es.ts +61 -4
- package/src/i18n/ja.ts +61 -4
- package/src/i18n/ko.ts +61 -4
- package/src/i18n/zh-cn.ts +61 -4
- package/src/i18n/zh-tw.ts +61 -4
- package/src/layout/desks.ts +1 -1
- package/src/layout/pane-manager/dock-tree.ts +18 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/market-data/coordinator/chart.ts +10 -4
- package/src/market-data/coordinator/index.ts +6 -3
- package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
- package/src/market-data/futures-contract.ts +151 -0
- package/src/market-data/hooks.tsx +10 -2
- package/src/market-data/market/format.ts +39 -21
- package/src/market-data/market/freshness.ts +81 -18
- package/src/market-data/market/status.ts +19 -23
- package/src/market-data/published-apac-sessions.ts +23 -1
- package/src/market-data/quotes/contributions.ts +3 -0
- package/src/market-data/quotes/metadata.ts +7 -24
- package/src/market-data/quotes/resolution.ts +2 -0
- package/src/market-data/quotes/screener-live-quotes.ts +5 -4
- package/src/market-data/result-types.ts +2 -0
- package/src/plugins/builtin/account-management/model.ts +1 -56
- package/src/plugins/builtin/account-management/pane.tsx +6 -4
- package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
- package/src/plugins/builtin/alerts/cli.ts +313 -47
- package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
- package/src/plugins/builtin/alerts/events.ts +23 -2
- package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +41 -9
- package/src/plugins/builtin/alerts/pane.tsx +8 -0
- package/src/plugins/builtin/alerts/research-builder.ts +8 -3
- package/src/plugins/builtin/alerts/research-rules.ts +18 -2
- package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
- package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
- package/src/plugins/builtin/alerts/scheduled.ts +254 -0
- package/src/plugins/builtin/alerts/targets.ts +92 -0
- package/src/plugins/builtin/analytics/index.tsx +4 -2
- package/src/plugins/builtin/analytics/risk-headless.ts +30 -12
- package/src/plugins/builtin/analytics/risk-math.ts +91 -27
- package/src/plugins/builtin/analytics/risk-model.ts +35 -5
- package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
- package/src/plugins/builtin/attention/index.tsx +2 -0
- package/src/plugins/builtin/attention/pane.tsx +3 -1
- package/src/plugins/builtin/attention/series.ts +26 -0
- package/src/plugins/builtin/awards/index.tsx +2 -0
- package/src/plugins/builtin/awards/pane.tsx +3 -1
- package/src/plugins/builtin/awards/series.ts +29 -0
- package/src/plugins/builtin/bond-calculator/model.ts +2 -1
- package/src/plugins/builtin/catalysts/pane.tsx +3 -1
- package/src/plugins/builtin/cds/client.ts +70 -13
- package/src/plugins/builtin/cds/headless.ts +116 -19
- package/src/plugins/builtin/cds/index.tsx +3 -1
- package/src/plugins/builtin/cds/model.ts +193 -31
- package/src/plugins/builtin/cds/pane.tsx +56 -14
- package/src/plugins/builtin/cds/series.ts +19 -0
- package/src/plugins/builtin/central-bank-rates/index.tsx +2 -0
- package/src/plugins/builtin/central-bank-rates/series.ts +42 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +192 -255
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
- package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
- package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
- package/src/plugins/builtin/chart-composer/headless.ts +8 -3
- package/src/plugins/builtin/chart-composer/index.tsx +9 -2
- package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
- package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
- package/src/plugins/builtin/chat/controller/index.ts +2 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
- package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
- package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
- package/src/plugins/builtin/cloud/askg/model.ts +27 -8
- package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
- package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
- package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
- package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
- package/src/plugins/builtin/company-attention/headless.ts +19 -5
- package/src/plugins/builtin/company-attention/index.tsx +2 -0
- package/src/plugins/builtin/company-attention/model.ts +26 -7
- package/src/plugins/builtin/company-attention/pane.tsx +12 -9
- package/src/plugins/builtin/company-attention/series.ts +52 -0
- package/src/plugins/builtin/company-kpis/pane.tsx +4 -2
- package/src/plugins/builtin/composite-plugins.ts +19 -13
- package/src/plugins/builtin/cot/index.tsx +3 -1
- package/src/plugins/builtin/cot/model.ts +40 -11
- package/src/plugins/builtin/cot/series.ts +27 -0
- package/src/plugins/builtin/credit-boards/index.tsx +2 -0
- package/src/plugins/builtin/credit-boards/series.ts +66 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -0
- package/src/plugins/builtin/credit-conditions/series.ts +26 -0
- package/src/plugins/builtin/credit-documents/headless.ts +31 -1
- package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
- package/src/plugins/builtin/crypto-board/headless.ts +2 -3
- package/src/plugins/builtin/crypto-board/index.tsx +2 -0
- package/src/plugins/builtin/crypto-board/model.ts +12 -0
- package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
- package/src/plugins/builtin/crypto-board/series.ts +48 -0
- package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/series.ts +45 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +2 -2
- package/src/plugins/builtin/dividend-yield/index.tsx +2 -0
- package/src/plugins/builtin/dividend-yield/series.ts +20 -0
- package/src/plugins/builtin/docs/blocks.tsx +181 -0
- package/src/plugins/builtin/docs/index.tsx +68 -0
- package/src/plugins/builtin/docs/pane.tsx +426 -0
- package/src/plugins/builtin/docs/targets.ts +72 -0
- package/src/plugins/builtin/docs/use-docs.ts +60 -0
- package/src/plugins/builtin/doe/index.tsx +2 -0
- package/src/plugins/builtin/doe/series.ts +62 -0
- package/src/plugins/builtin/earnings/client.ts +3 -2
- package/src/plugins/builtin/earnings/format.ts +1 -5
- package/src/plugins/builtin/earnings/headless.ts +58 -6
- package/src/plugins/builtin/earnings/history-model.ts +15 -1
- package/src/plugins/builtin/earnings/history-table.ts +13 -2
- package/src/plugins/builtin/earnings/history.tsx +7 -3
- package/src/plugins/builtin/econ/fred-catalog.ts +189 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
- package/src/plugins/builtin/econ/index.tsx +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
- package/src/plugins/builtin/exposure/holdings.ts +1 -1
- package/src/plugins/builtin/exposure/pane.tsx +3 -1
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/fear-greed/series.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +43 -3
- package/src/plugins/builtin/futures/model.ts +1 -1
- package/src/plugins/builtin/futures/returns.ts +1 -1
- package/src/plugins/builtin/futures/series.ts +13 -0
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +70 -9
- package/src/plugins/builtin/futures-curve/index.tsx +1 -1
- package/src/plugins/builtin/futures-curve/model.ts +66 -11
- package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
- package/src/plugins/builtin/fx-matrix/index.tsx +1 -1
- package/src/plugins/builtin/gpu/board.tsx +13 -10
- package/src/plugins/builtin/gpu/changes.tsx +4 -4
- package/src/plugins/builtin/gpu/client.ts +19 -1
- package/src/plugins/builtin/gpu/equities.tsx +14 -10
- package/src/plugins/builtin/gpu/headless.ts +56 -25
- package/src/plugins/builtin/gpu/history.tsx +3 -3
- package/src/plugins/builtin/gpu/index.tsx +2 -0
- package/src/plugins/builtin/gpu/model.ts +101 -16
- package/src/plugins/builtin/gpu/pane.tsx +28 -12
- package/src/plugins/builtin/gpu/series.ts +38 -0
- package/src/plugins/builtin/help/function-card.tsx +25 -6
- package/src/plugins/builtin/help/index.tsx +17 -8
- package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
- package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
- package/src/plugins/builtin/holders/client.ts +22 -3
- package/src/plugins/builtin/holders/format.ts +18 -3
- package/src/plugins/builtin/holders/headless.ts +36 -133
- package/src/plugins/builtin/holders/pane.tsx +11 -4
- package/src/plugins/builtin/holders/report-header.ts +40 -5
- package/src/plugins/builtin/holders/report-rows.ts +240 -0
- package/src/plugins/builtin/insider/client.ts +35 -20
- package/src/plugins/builtin/insider/headless.ts +41 -11
- package/src/plugins/builtin/insider/index.tsx +26 -9
- package/src/plugins/builtin/insider/insider-data.ts +10 -0
- package/src/plugins/builtin/insider/issuer.ts +49 -0
- package/src/plugins/builtin/insider/model.ts +3 -0
- package/src/plugins/builtin/iv-history/client.ts +14 -4
- package/src/plugins/builtin/iv-history/headless.ts +7 -4
- package/src/plugins/builtin/iv-history/index.tsx +2 -0
- package/src/plugins/builtin/iv-history/model.ts +111 -7
- package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
- package/src/plugins/builtin/iv-history/series.ts +29 -0
- package/src/plugins/builtin/jobs/headless.ts +28 -3
- package/src/plugins/builtin/jobs/index.tsx +3 -1
- package/src/plugins/builtin/jobs/series.ts +19 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
- package/src/plugins/builtin/macro-day/client.ts +44 -3
- package/src/plugins/builtin/macro-day/headless.ts +13 -10
- package/src/plugins/builtin/macro-day/model.ts +56 -10
- package/src/plugins/builtin/macro-day/pane.tsx +30 -20
- package/src/plugins/builtin/market-heatmap/model.ts +19 -11
- package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
- package/src/plugins/builtin/market-movers/client.ts +7 -5
- package/src/plugins/builtin/market-movers/index.tsx +3 -2
- package/src/plugins/builtin/market-movers/model.ts +30 -0
- package/src/plugins/builtin/market-movers/screener.ts +31 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
- package/src/plugins/builtin/market-valuation/index.tsx +3 -3
- package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
- package/src/plugins/builtin/members/headless.ts +7 -2
- package/src/plugins/builtin/members/model.ts +31 -3
- package/src/plugins/builtin/members/pane.tsx +3 -6
- package/src/plugins/builtin/mna/headless.ts +15 -3
- package/src/plugins/builtin/money-markets/index.tsx +2 -0
- package/src/plugins/builtin/money-markets/series.ts +41 -0
- package/src/plugins/builtin/notes/cli.ts +12 -7
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
- package/src/plugins/builtin/options-positioning/client.ts +7 -3
- package/src/plugins/builtin/options-positioning/headless.ts +1 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +2 -1
- package/src/plugins/builtin/options-scenario/headless.ts +1 -0
- package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
- package/src/plugins/builtin/perps/client.ts +40 -6
- package/src/plugins/builtin/perps/equity-row.tsx +33 -11
- package/src/plugins/builtin/perps/freshness.ts +66 -0
- package/src/plugins/builtin/perps/headless.ts +45 -30
- package/src/plugins/builtin/perps/index.tsx +18 -1
- package/src/plugins/builtin/perps/model.ts +20 -5
- package/src/plugins/builtin/perps/pane.tsx +76 -20
- package/src/plugins/builtin/perps/series.ts +76 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +67 -61
- package/src/plugins/builtin/plugin-module.ts +3 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +273 -0
- package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +331 -65
- package/src/plugins/builtin/portfolio-list/cli/render.ts +200 -31
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +24 -0
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -47
- package/src/plugins/builtin/portfolio-list/column-values.ts +1 -1
- package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +34 -4
- package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -1
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +17 -1
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +7 -1
- package/src/plugins/builtin/power/client.ts +16 -0
- package/src/plugins/builtin/power/evidence.ts +6 -2
- package/src/plugins/builtin/power/headless.ts +71 -7
- package/src/plugins/builtin/power/index.tsx +4 -1
- package/src/plugins/builtin/power/model.ts +72 -1
- package/src/plugins/builtin/power/pane.tsx +32 -9
- package/src/plugins/builtin/rate-path/headless.ts +11 -3
- package/src/plugins/builtin/realized-vol/headless.ts +5 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
- package/src/plugins/builtin/research/event-model.ts +68 -24
- package/src/plugins/builtin/research/events-headless.ts +2 -1
- package/src/plugins/builtin/research/headless.ts +2 -1
- package/src/plugins/builtin/seasonality/client.ts +15 -7
- package/src/plugins/builtin/seasonality/headless.ts +36 -12
- package/src/plugins/builtin/seasonality/index.tsx +2 -2
- package/src/plugins/builtin/seasonality/model.ts +119 -0
- package/src/plugins/builtin/seasonality/pane.tsx +108 -37
- package/src/plugins/builtin/series-inventory.ts +91 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
- package/src/plugins/builtin/shared/command-tab.ts +30 -12
- package/src/plugins/builtin/shared/earnings-report.ts +55 -0
- package/src/plugins/builtin/shared/fred-points.ts +20 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
- package/src/plugins/builtin/shared/open-description.ts +29 -0
- package/src/plugins/builtin/shared/report-freshness.ts +9 -0
- package/src/plugins/builtin/shared/series-warning.ts +5 -0
- package/src/plugins/builtin/short-interest/headless.ts +1 -0
- package/src/plugins/builtin/short-interest/index.tsx +2 -0
- package/src/plugins/builtin/short-interest/series.ts +36 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
- package/src/plugins/builtin/short-volume/headless.ts +1 -0
- package/src/plugins/builtin/short-volume/index.tsx +2 -0
- package/src/plugins/builtin/short-volume/series.ts +23 -0
- package/src/plugins/builtin/social-mentions/index.tsx +2 -0
- package/src/plugins/builtin/social-mentions/series.ts +23 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
- package/src/plugins/builtin/supply-chain/pane.tsx +3 -1
- package/src/plugins/builtin/thirteenf/api.ts +1 -0
- package/src/plugins/builtin/thirteenf/data.ts +6 -4
- package/src/plugins/builtin/thirteenf/headless.ts +217 -36
- package/src/plugins/builtin/thirteenf/model.ts +39 -1
- package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
- package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
- package/src/plugins/builtin/thirteenf/types.ts +2 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +48 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
- package/src/plugins/builtin/time-sales/headless.ts +1 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
- package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
- package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
- package/src/plugins/builtin/vol-surface/headless.ts +1 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -0
- package/src/plugins/builtin/volatility/series.ts +84 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
- package/src/plugins/builtin/world-venue-map/index.ts +2 -1
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +8 -4
- package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
- package/src/plugins/builtin/yield-curve/chart.ts +2 -2
- package/src/plugins/builtin/yield-curve/curves.ts +21 -8
- package/src/plugins/builtin/yield-curve/headless.ts +71 -18
- package/src/plugins/builtin/yield-curve/index.tsx +3 -1
- package/src/plugins/builtin/yield-curve/series.ts +86 -0
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/registry/index.ts +89 -4
- package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
- package/src/portfolio/position-value.ts +40 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
- package/src/public/time-series.ts +17 -0
- package/src/react/auto-refresh.ts +13 -3
- package/src/remote/app-host.tsx +20 -4
- package/src/remote/confirm-change.tsx +85 -0
- package/src/remote/controller-utils.ts +8 -1
- package/src/remote/controller.ts +123 -6
- package/src/remote/pane-screenshot.ts +47 -0
- package/src/remote/schema.ts +62 -0
- package/src/remote/types.ts +30 -0
- package/src/remote/watchlist.ts +306 -0
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/dom/data-table/frozen-column.ts +3 -1
- package/src/renderers/dom/data-table/index.tsx +33 -6
- package/src/renderers/dom/data-table/row.tsx +27 -1
- package/src/renderers/dom/desktop/control-styles.ts +63 -1
- package/src/renderers/dom/desktop/controls.tsx +30 -23
- package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -0
- package/src/renderers/dom/host/box.tsx +3 -0
- package/src/renderers/dom/host/chart-surface.tsx +6 -2
- package/src/renderers/dom/host/live-frame.ts +6 -2
- package/src/renderers/dom/host/overflow-fade.ts +14 -1
- package/src/renderers/dom/host/size-observer.ts +26 -0
- package/src/renderers/dom/host/surface-3d.tsx +7 -2
- package/src/renderers/dom/host/text.tsx +1 -1
- package/src/renderers/dom/styles.css +61 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +17 -2
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +19 -11
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/series/capability.ts +75 -0
- package/src/series/csv.ts +160 -0
- package/src/series/files.ts +107 -0
- package/src/series/registry.ts +198 -0
- package/src/series/resolve.ts +107 -0
- package/src/series/standalone.ts +48 -0
- package/src/series/types.ts +145 -0
- package/src/series/validate.ts +171 -0
- package/src/sources/gloomberb-cloud/index.ts +7 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +1 -0
- package/src/sources/history-result.ts +1 -0
- package/src/sources/listing-symbols.ts +4 -1
- package/src/sources/provider-router/batches.ts +4 -1
- package/src/sources/provider-router/cache.ts +4 -2
- package/src/sources/provider-router/cached-routes.ts +1 -1
- package/src/sources/provider-router/financial-routes.ts +1 -0
- package/src/sources/provider-router/financials.ts +13 -0
- package/src/sources/provider-router/history.ts +43 -25
- package/src/sources/provider-router/search.ts +1 -0
- package/src/sync/core-contributors.ts +1 -0
- package/src/tickers/company-name.ts +75 -0
- package/src/tickers/instrument-kind.ts +25 -0
- package/src/tickers/open-target.ts +3 -2
- package/src/tickers/search/index.ts +45 -7
- package/src/tickers/search/ranking.ts +33 -17
- package/src/tickers/search/types.ts +7 -1
- package/src/tickers/us-listing.ts +34 -0
- package/src/time-series/hooks.ts +9 -0
- package/src/time-series/resolve.ts +95 -16
- package/src/time-series/types.ts +4 -2
- package/src/time-series/use-chart-resolution.ts +32 -0
- package/src/types/config.ts +6 -0
- package/src/types/data-provider.ts +5 -0
- package/src/types/financials.ts +52 -1
- package/src/types/headless.ts +61 -0
- package/src/types/plugin.ts +19 -0
- package/src/types/price-history.ts +7 -0
- package/src/types/ticker.ts +14 -0
- package/src/ui/host.tsx +18 -0
- package/src/ui/live-resize.ts +81 -0
- package/src/ui/live-stretch.ts +121 -0
- package/src/ui/measured-cells.ts +58 -0
- package/src/ui/pane-fill.ts +11 -0
- package/src/utils/basis-points.ts +17 -0
- package/src/utils/cli-output.ts +7 -1
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/crypto-pair.ts +20 -0
- package/src/utils/currency-pair.ts +19 -0
- package/src/utils/decimal-comma.ts +19 -0
- package/src/utils/dom-screenshot-css.ts +265 -0
- package/src/utils/dom-screenshot-rasters.ts +276 -0
- package/src/utils/dom-screenshot.ts +437 -52
- package/src/utils/exchange-rate-snapshot.ts +6 -2
- package/src/utils/exchanges.ts +107 -1
- package/src/utils/format.ts +16 -3
- package/src/utils/fundamentals.ts +66 -0
- package/src/utils/futures-generic.ts +6 -12
- package/src/utils/fx-market-hours.ts +15 -5
- package/src/utils/price-history.ts +99 -42
- package/src/utils/sec-eps-basis.ts +24 -11
- package/src/utils/sec.ts +61 -7
- package/src/utils/utc-time.ts +51 -4
- package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
- /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
package/src/utils/exchanges.ts
CHANGED
|
@@ -20,12 +20,16 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
20
20
|
ARCA: "ARCA",
|
|
21
21
|
ARCX: "ARCA",
|
|
22
22
|
BATS: "BATS",
|
|
23
|
-
// Cboe's US equities listing venue (BZX); quotes label it "CBOE US".
|
|
23
|
+
// Cboe's US equities listing venue (BZX); quotes label it "CBOE US" or BTS.
|
|
24
24
|
"CBOE US": "BATS",
|
|
25
|
+
BTS: "BATS",
|
|
25
26
|
TYO: "JPX",
|
|
27
|
+
// Toronto, as brokers and quote sites spell it; typed after a symbol it can
|
|
28
|
+
// also be Tokyo (SHARED_EXCHANGE_CODES).
|
|
26
29
|
TSE: "TSX",
|
|
27
30
|
TSEJ: "JPX",
|
|
28
31
|
JPX: "JPX",
|
|
32
|
+
XTKS: "JPX",
|
|
29
33
|
HKG: "HKEX",
|
|
30
34
|
HKEX: "HKEX",
|
|
31
35
|
SEHK: "HKEX",
|
|
@@ -40,6 +44,7 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
40
44
|
TSXV: "TSXV",
|
|
41
45
|
XTSX: "TSXV",
|
|
42
46
|
VAN: "TSXV",
|
|
47
|
+
CVE: "TSXV",
|
|
43
48
|
CN: "CSE",
|
|
44
49
|
CNSX: "CSE",
|
|
45
50
|
XCNQ: "CSE",
|
|
@@ -48,6 +53,7 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
48
53
|
XASX: "ASX",
|
|
49
54
|
NZX: "NZX",
|
|
50
55
|
XNZE: "NZX",
|
|
56
|
+
NZE: "NZX",
|
|
51
57
|
SIX: "SWX",
|
|
52
58
|
SWX: "SWX",
|
|
53
59
|
EBS: "SWX",
|
|
@@ -60,6 +66,7 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
60
66
|
GER: "XETRA",
|
|
61
67
|
FSE: "FWB2",
|
|
62
68
|
FSX: "FWB2",
|
|
69
|
+
FRA: "FWB2",
|
|
63
70
|
XFRA: "FWB2",
|
|
64
71
|
FWB: "FWB2",
|
|
65
72
|
FWB2: "FWB2",
|
|
@@ -112,14 +119,19 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
112
119
|
SSE: "SSE",
|
|
113
120
|
XSHG: "SSE",
|
|
114
121
|
SHH: "SSE",
|
|
122
|
+
SHA: "SSE",
|
|
115
123
|
SZSE: "SZSE",
|
|
116
124
|
XSHE: "SZSE",
|
|
117
125
|
SHE: "SZSE",
|
|
126
|
+
SHZ: "SZSE",
|
|
127
|
+
// KOSPI; typed after a symbol it can also be KOSDAQ, which the Korea Exchange runs too.
|
|
118
128
|
KRX: "KRX",
|
|
119
129
|
XKRX: "KRX",
|
|
120
130
|
KOSPI: "KRX",
|
|
131
|
+
KSC: "KRX",
|
|
121
132
|
KOSDAQ: "KOSDAQ",
|
|
122
133
|
XKOS: "KOSDAQ",
|
|
134
|
+
KOE: "KOSDAQ",
|
|
123
135
|
SGX: "SGX",
|
|
124
136
|
XSES: "SGX",
|
|
125
137
|
BMV: "BMV",
|
|
@@ -127,6 +139,7 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
127
139
|
B3: "B3",
|
|
128
140
|
BVMF: "B3",
|
|
129
141
|
B3SA: "B3",
|
|
142
|
+
SAO: "B3",
|
|
130
143
|
BUE: "BYMA",
|
|
131
144
|
BCBA: "BYMA",
|
|
132
145
|
JSE: "JSE",
|
|
@@ -138,9 +151,52 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
138
151
|
QE: "QE",
|
|
139
152
|
QSE: "QE",
|
|
140
153
|
DSMD: "QE",
|
|
154
|
+
// Bahrain Bourse, which Gloom Cloud names BHB.
|
|
155
|
+
BHB: "BHB",
|
|
156
|
+
XBAH: "BHB",
|
|
157
|
+
BAHRAIN: "BHB",
|
|
158
|
+
"BAHRAIN BOURSE": "BHB",
|
|
141
159
|
DFM: "DFM",
|
|
142
160
|
XDFM: "DFM",
|
|
161
|
+
// The Muscat Stock Exchange, which Gloom Cloud names MSX.
|
|
162
|
+
MSX: "MSX",
|
|
163
|
+
XMUS: "MSX",
|
|
164
|
+
MSM: "MSX",
|
|
165
|
+
MUSCAT: "MSX",
|
|
166
|
+
OMAN: "MSX",
|
|
167
|
+
// Boursa Kuwait, which Gloom Cloud names KUWAIT.
|
|
168
|
+
KUWAIT: "KUWAIT",
|
|
169
|
+
XKUW: "KUWAIT",
|
|
170
|
+
BOURSA: "KUWAIT",
|
|
171
|
+
"BOURSA KUWAIT": "KUWAIT",
|
|
172
|
+
// The Egyptian Exchange, which Gloom Cloud names EGX.
|
|
173
|
+
EGX: "EGX",
|
|
174
|
+
CAI: "EGX",
|
|
175
|
+
XCAI: "EGX",
|
|
176
|
+
EGYPT: "EGX",
|
|
177
|
+
CAIRO: "EGX",
|
|
143
178
|
CCC: "CCC",
|
|
179
|
+
// Futures venues, by the code search gives each (GC=F on CMX). Quotes spell
|
|
180
|
+
// the venue out (COMEX, NY MERCANTILE); both spellings are one venue, or the
|
|
181
|
+
// quote for GC=F:CMX reads as another venue's and is dropped.
|
|
182
|
+
CME: "CME",
|
|
183
|
+
CBT: "CBT",
|
|
184
|
+
CBOT: "CBT",
|
|
185
|
+
NYM: "NYM",
|
|
186
|
+
NYMEX: "NYM",
|
|
187
|
+
"NY MERCANTILE": "NYM",
|
|
188
|
+
CMX: "CMX",
|
|
189
|
+
COMEX: "CMX",
|
|
190
|
+
NYB: "NYB",
|
|
191
|
+
NYBOT: "NYB",
|
|
192
|
+
"ICE FUTURES": "NYB",
|
|
193
|
+
CFE: "CFE",
|
|
194
|
+
"CBOE FUTURES": "CFE",
|
|
195
|
+
// The currency venue: EURUSD:CCY and EURUSD:FX are the pair EURUSD=X. Pairs
|
|
196
|
+
// saved from an older search answer are on PHYSICAL CURRENCY.
|
|
197
|
+
CCY: "CCY",
|
|
198
|
+
FX: "CCY",
|
|
199
|
+
"PHYSICAL CURRENCY": "CCY",
|
|
144
200
|
};
|
|
145
201
|
|
|
146
202
|
const PUBLIC_EXCHANGE_ALIASES: Record<string, string> = {
|
|
@@ -231,7 +287,11 @@ export const EXCHANGE_TIME_ZONES: Record<string, string> = {
|
|
|
231
287
|
TASE: "Asia/Jerusalem",
|
|
232
288
|
TADAWUL: "Asia/Riyadh",
|
|
233
289
|
QE: "Asia/Qatar",
|
|
290
|
+
BHB: "Asia/Bahrain",
|
|
234
291
|
DFM: "Asia/Dubai",
|
|
292
|
+
MSX: "Asia/Muscat",
|
|
293
|
+
KUWAIT: "Asia/Kuwait",
|
|
294
|
+
EGX: "Africa/Cairo",
|
|
235
295
|
CCC: "UTC",
|
|
236
296
|
};
|
|
237
297
|
|
|
@@ -287,7 +347,16 @@ const EXCHANGE_NAMES: Record<string, string> = {
|
|
|
287
347
|
TASE: "Tel Aviv Stock Exchange",
|
|
288
348
|
TADAWUL: "Saudi Exchange",
|
|
289
349
|
QE: "Qatar Stock Exchange",
|
|
350
|
+
BHB: "Bahrain Bourse",
|
|
290
351
|
DFM: "Dubai Financial Market",
|
|
352
|
+
MSX: "Muscat Stock Exchange",
|
|
353
|
+
KUWAIT: "Boursa Kuwait",
|
|
354
|
+
EGX: "Egyptian Exchange",
|
|
355
|
+
CBT: "CBOT",
|
|
356
|
+
NYM: "NYMEX",
|
|
357
|
+
CMX: "COMEX",
|
|
358
|
+
NYB: "ICE Futures U.S.",
|
|
359
|
+
CFE: "Cboe Futures Exchange",
|
|
291
360
|
};
|
|
292
361
|
|
|
293
362
|
const KNOWN_EXCHANGE_CODES: ReadonlySet<string> = new Set([
|
|
@@ -323,6 +392,43 @@ export function canonicalExchange(value?: string): string {
|
|
|
323
392
|
return CANONICAL_EXCHANGE_ALIASES[normalized] ?? normalized;
|
|
324
393
|
}
|
|
325
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Codes people type for more than one venue, the venue `canonicalExchange`
|
|
397
|
+
* reads them as first. TSE is the Tokyo Stock Exchange's own short name, and
|
|
398
|
+
* the code brokers and quote sites give Toronto; KRX is the Korea Exchange,
|
|
399
|
+
* which runs both KOSPI and KOSDAQ; EURONEXT runs Paris, Amsterdam, Brussels,
|
|
400
|
+
* Lisbon, Oslo and Milan. Data that arrives with one (a broker's contract)
|
|
401
|
+
* keeps the first venue; a listing typed with one is the venue its symbol
|
|
402
|
+
* trades on.
|
|
403
|
+
*/
|
|
404
|
+
const SHARED_EXCHANGE_CODES: Record<string, readonly string[]> = {
|
|
405
|
+
TSE: ["TSX", "JPX"],
|
|
406
|
+
KRX: ["KRX", "KOSDAQ"],
|
|
407
|
+
EURONEXT: ["AMS", "EPA", "BRU", "LIS", "OSL", "BIT"],
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/** Whether a typed exchange code names more than one venue (TSE: Tokyo or Toronto). */
|
|
411
|
+
export function isSharedExchangeCode(value?: string): boolean {
|
|
412
|
+
return normalizeExchange(value) in SHARED_EXCHANGE_CODES;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Every venue a typed exchange code can name, as canonical codes: TSE is
|
|
417
|
+
* TSX or JPX, PAR is EPA. Empty for no code.
|
|
418
|
+
*/
|
|
419
|
+
export function exchangeCodeVenues(value?: string): string[] {
|
|
420
|
+
const normalized = normalizeExchange(value);
|
|
421
|
+
if (!normalized) return [];
|
|
422
|
+
return [...(SHARED_EXCHANGE_CODES[normalized] ?? [canonicalExchange(normalized)])];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** The exchange code after a listing key's colon, as typed: TSE for 7203:TSE; "" when there is none. */
|
|
426
|
+
export function typedExchangeCode(key: string): string {
|
|
427
|
+
const normalized = normalizeSymbol(key);
|
|
428
|
+
const separator = normalized.lastIndexOf(":");
|
|
429
|
+
return separator > 0 ? normalized.slice(separator + 1) : "";
|
|
430
|
+
}
|
|
431
|
+
|
|
326
432
|
/**
|
|
327
433
|
* The US primary listing venues, as canonical exchanges. All of them trade the
|
|
328
434
|
* US regular and extended-hours sessions.
|
package/src/utils/format.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const currencyFormatters = new Map<string, Intl.NumberFormat>();
|
|
2
2
|
const numberFormatters = new Map<number, Intl.NumberFormat>();
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/** Currencies whose minor unit is a thousandth (ISO 4217 exponent 3): an Omani share is OMR 1.366, not OMR 1.37.
|
|
5
|
+
* IQD is left out on purpose: it has the exponent in ISO 4217 but trades in whole units. */
|
|
6
|
+
const THREE_DECIMAL_CURRENCIES = new Set(["BHD", "JOD", "KWD", "LYD", "OMR", "TND"]);
|
|
7
|
+
|
|
8
|
+
/** Decimals a price or amount in this currency is shown to: 3 for the thousandth currencies, 2 for everything else. */
|
|
9
|
+
export function currencyPriceDigits(currency: string | undefined): 2 | 3 {
|
|
10
|
+
return currency != null && THREE_DECIMAL_CURRENCIES.has(currency.trim().toUpperCase()) ? 3 : 2;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getCurrencyFormatter(currency: string, maximumFractionDigits: number = currencyPriceDigits(currency)): Intl.NumberFormat {
|
|
5
14
|
const key = `${currency}:${maximumFractionDigits}`;
|
|
6
15
|
let formatter = currencyFormatters.get(key);
|
|
7
16
|
if (!formatter) {
|
|
@@ -9,7 +18,7 @@ function getCurrencyFormatter(currency: string, maximumFractionDigits = 2): Intl
|
|
|
9
18
|
style: "currency",
|
|
10
19
|
currency,
|
|
11
20
|
// Whole units (0) drop the cents rather than throw.
|
|
12
|
-
minimumFractionDigits: Math.min(
|
|
21
|
+
minimumFractionDigits: Math.min(currencyPriceDigits(currency), maximumFractionDigits),
|
|
13
22
|
maximumFractionDigits,
|
|
14
23
|
});
|
|
15
24
|
currencyFormatters.set(key, formatter);
|
|
@@ -30,7 +39,11 @@ function getNumberFormatter(decimals: number): Intl.NumberFormat {
|
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
/** Format a number as currency (e.g., $1,234.56) */
|
|
33
|
-
export function formatCurrency(
|
|
42
|
+
export function formatCurrency(
|
|
43
|
+
value: number | undefined,
|
|
44
|
+
currency = "USD",
|
|
45
|
+
maximumFractionDigits: number = currencyPriceDigits(currency),
|
|
46
|
+
): string {
|
|
34
47
|
if (value == null || !Number.isFinite(value)) return "—";
|
|
35
48
|
return getCurrencyFormatter(currency, maximumFractionDigits).format(value);
|
|
36
49
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Fundamentals, TickerFinancials } from "../types/financials";
|
|
2
|
+
import { resolveCurrencyUnit } from "./currency-units";
|
|
2
3
|
|
|
3
4
|
export const RETRACTABLE_VALUATION_FIELDS = ["enterpriseValue", "enterpriseToRevenue"] as const;
|
|
4
5
|
|
|
@@ -29,3 +30,68 @@ export function fundamentalsCurrency(
|
|
|
29
30
|
): string | undefined {
|
|
30
31
|
return financials?.fundamentals?.financialCurrency?.trim() || financials?.financialCurrency?.trim() || undefined;
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The currency of the trailing EPS: the one stated for it when it differs from the block's amounts (an EPS converted
|
|
36
|
+
* to the listing's currency beside revenue left in the reporting one), else the block's.
|
|
37
|
+
*/
|
|
38
|
+
export function fundamentalsEpsCurrency(
|
|
39
|
+
financials: Pick<TickerFinancials, "fundamentals" | "financialCurrency"> | null | undefined,
|
|
40
|
+
): string | undefined {
|
|
41
|
+
return financials?.fundamentals?.epsCurrency?.trim() || fundamentalsCurrency(financials);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* "TTM converted from EUR (1 EUR = 1.1257 USD, 2026-10-09); EPS at report-date rates.": what a converted trailing
|
|
48
|
+
* block was converted from, at which rate and when. Undefined for a block in the currency the company reports in.
|
|
49
|
+
*/
|
|
50
|
+
export function ttmConversionNote(fundamentals: Pick<Fundamentals, "financialCurrency" | "ttmConversion"> | undefined): string | undefined {
|
|
51
|
+
const conversion = fundamentals?.ttmConversion;
|
|
52
|
+
const from = conversion?.from?.trim();
|
|
53
|
+
const to = fundamentals?.financialCurrency?.trim();
|
|
54
|
+
if (!conversion || !from || !to || from === to) return undefined;
|
|
55
|
+
const rate = typeof conversion.rate === "number" && Number.isFinite(conversion.rate) && conversion.rate > 0
|
|
56
|
+
? `1 ${from} = ${Number(conversion.rate.toPrecision(5))} ${to}`
|
|
57
|
+
: undefined;
|
|
58
|
+
const asOf = conversion.asOf && ISO_DAY.test(conversion.asOf) ? conversion.asOf : undefined;
|
|
59
|
+
const basis = [rate, asOf].filter(Boolean).join(", ");
|
|
60
|
+
const eps = conversion.eps === "report_date" ? "; EPS at report-date rates" : "";
|
|
61
|
+
return `TTM converted from ${from}${basis ? ` (${basis})` : ""}${eps}.`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* "EPS (TTM) in USD, converted from CLP at report-date rates.": an EPS in another currency than the trailing amounts
|
|
66
|
+
* beside it. Undefined when they share one.
|
|
67
|
+
*/
|
|
68
|
+
export function epsConversionNote(fundamentals: Pick<Fundamentals, "financialCurrency" | "epsCurrency"> | undefined): string | undefined {
|
|
69
|
+
const eps = fundamentals?.epsCurrency?.trim();
|
|
70
|
+
const amounts = fundamentals?.financialCurrency?.trim();
|
|
71
|
+
return eps && amounts && eps !== amounts ? `EPS (TTM) in ${eps}, converted from ${amounts} at report-date rates.` : undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type PriceEarningsBasis = Pick<Fundamentals, "financialCurrency" | "ttmConversion" | "epsCurrency" | "trailingPEConversion">;
|
|
75
|
+
|
|
76
|
+
/** The trailing EPS in the price's currency: converted at the rate the served P/E used when it says so. */
|
|
77
|
+
export function priceEarningsEps(fundamentals: Pick<Fundamentals, "eps" | "trailingPEConversion"> | undefined): number | undefined {
|
|
78
|
+
const eps = fundamentals?.eps;
|
|
79
|
+
const rate = fundamentals?.trailingPEConversion?.rate;
|
|
80
|
+
return typeof eps === "number" && typeof rate === "number" && Number.isFinite(rate) && rate > 0 ? eps * rate : eps;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The trailing P/E's footnote: the rate its EPS was converted at when that EPS is in another currency than the
|
|
85
|
+
* price, or that it divides a spot price by EPS converted at report-date rates.
|
|
86
|
+
*/
|
|
87
|
+
export function convertedPriceEarningsNote(fundamentals: PriceEarningsBasis | undefined, quoteCurrency?: string): string | undefined {
|
|
88
|
+
const conversion = fundamentals?.trailingPEConversion;
|
|
89
|
+
const from = conversion?.from?.trim();
|
|
90
|
+
const to = resolveCurrencyUnit(quoteCurrency).currency;
|
|
91
|
+
if (from && to && from !== to && typeof conversion?.rate === "number" && Number.isFinite(conversion.rate) && conversion.rate > 0) {
|
|
92
|
+
return `P/E (TTM): price over EPS converted at 1 ${from} = ${Number(conversion.rate.toPrecision(5))} ${to}.`;
|
|
93
|
+
}
|
|
94
|
+
return (ttmConversionNote(fundamentals) && fundamentals?.ttmConversion?.eps === "report_date") || epsConversionNote(fundamentals)
|
|
95
|
+
? "P/E (TTM): spot price over EPS at report-date rates."
|
|
96
|
+
: undefined;
|
|
97
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* ratio-adjusts, A difference-adjusts, neither leaves prices as traded.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { parsePublicTickerKey } from "./exchanges";
|
|
10
|
+
import { canonicalExchange, parsePublicTickerKey } from "./exchanges";
|
|
11
11
|
|
|
12
12
|
export type FuturesGenericRoll =
|
|
13
13
|
| { rule: "open-interest" }
|
|
@@ -36,11 +36,6 @@ const ROOT_VENUES: Readonly<Record<string, string>> = Object.fromEntries(([
|
|
|
36
36
|
["CFE", ["VX"]],
|
|
37
37
|
] as const).flatMap(([venue, roots]) => roots.map((root) => [root, venue])));
|
|
38
38
|
|
|
39
|
-
/** Other names quotes give each venue, which a listing may carry as its exchange. */
|
|
40
|
-
const VENUE_NAMES: Readonly<Record<string, readonly string[]>> = {
|
|
41
|
-
NYM: ["NYMEX", "NY MERCANTILE"], CBT: ["CBOT"], CMX: ["COMEX"], NYB: ["ICE FUTURES", "NYBOT"], CFE: ["CBOE FUTURES"],
|
|
42
|
-
};
|
|
43
|
-
|
|
44
39
|
/** Bloomberg's codes where they differ from the exchange's. */
|
|
45
40
|
const ALIASES: Readonly<Record<string, string>> = {
|
|
46
41
|
TY: "ZN", US: "ZB", FV: "ZF", TU: "ZT", WN: "UB", DM: "YM", FF: "ZQ", SFR: "SR3",
|
|
@@ -83,9 +78,9 @@ export function futuresGenericListing(symbol: string, exchange?: string | null):
|
|
|
83
78
|
const key = parsePublicTickerKey(symbol);
|
|
84
79
|
const generic = parseFuturesGeneric(key.symbol);
|
|
85
80
|
if (!generic) return null;
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
return !venue || venue ===
|
|
81
|
+
// The alias table names each venue by its code, however a quote spells it (NYMEX is NYM).
|
|
82
|
+
const venue = key.exchange || canonicalExchange(exchange ?? "");
|
|
83
|
+
return !venue || venue === ROOT_VENUES[generic.root] ? generic : null;
|
|
89
84
|
}
|
|
90
85
|
|
|
91
86
|
const FUTURES_VENUES = new Set(Object.values(ROOT_VENUES));
|
|
@@ -98,9 +93,8 @@ const FUTURES_VENUES = new Set(Object.values(ROOT_VENUES));
|
|
|
98
93
|
*/
|
|
99
94
|
export function futuresListingVenue(symbol: string, exchange?: string | null): { venue: string; root: string | null } | null {
|
|
100
95
|
const ticker = symbol.trim().toUpperCase();
|
|
101
|
-
const named = (exchange ?? "")
|
|
102
|
-
const venue = FUTURES_VENUES.has(named) ? named
|
|
103
|
-
: Object.entries(VENUE_NAMES).find(([, names]) => names.includes(named))?.[0] ?? null;
|
|
96
|
+
const named = canonicalExchange(exchange ?? "");
|
|
97
|
+
const venue = FUTURES_VENUES.has(named) ? named : null;
|
|
104
98
|
const coded = /^([A-Z0-9]{1,4}?)(?:=F|[FGHJKMNQUVXZ]\d{2}(?:\.[A-Z]+)?)$/.exec(ticker)?.[1]
|
|
105
99
|
?? /^([A-Z]{2})-Y\.[A-Z]+$/.exec(ticker)?.[1];
|
|
106
100
|
const root = futuresGenericListing(ticker, venue ?? named)?.root ?? (coded ? ALIASES[coded] ?? coded : null);
|
|
@@ -29,20 +29,30 @@ export function isFxMarketOpen(time: number): boolean {
|
|
|
29
29
|
return true;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/** The latest
|
|
33
|
-
|
|
32
|
+
/** The latest 17:00 New York on `weekday` (0 is Sunday) at or before `time`. */
|
|
33
|
+
function latestNewYorkFivePm(time: number, weekday: number): number | null {
|
|
34
34
|
if (!Number.isFinite(time)) return null;
|
|
35
35
|
const { year, month, day } = zonedDateTimeParts(time, NEW_YORK);
|
|
36
36
|
const today = Date.UTC(year, month - 1, day);
|
|
37
37
|
for (let offset = 0; offset <= 7; offset += 1) {
|
|
38
38
|
const date = new Date(today - offset * DAY_MS);
|
|
39
|
-
if (date.getUTCDay() !==
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
39
|
+
if (date.getUTCDay() !== weekday) continue;
|
|
40
|
+
const at = zonedWallClockToUtcMs(NEW_YORK, date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate(), 17, 0, 0);
|
|
41
|
+
if (at <= time) return at;
|
|
42
42
|
}
|
|
43
43
|
return null;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/** The latest close of the FX week, Friday 17:00 New York, at or before `time`. */
|
|
47
|
+
export function latestFxWeekClose(time: number): number | null {
|
|
48
|
+
return latestNewYorkFivePm(time, 5);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The latest open of the FX week, Sunday 17:00 New York, at or before `time`. */
|
|
52
|
+
export function latestFxWeekOpen(time: number): number | null {
|
|
53
|
+
return latestNewYorkFivePm(time, 0);
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
/**
|
|
47
57
|
* When the closure the market is in began (Friday 17:00 New York time); null
|
|
48
58
|
* while it trades. That is `latestFxWeekClose` for every moment the week is
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import type { PricePoint, TickerFinancials } from "../types/financials";
|
|
2
|
-
import { canonicalExchange
|
|
2
|
+
import { canonicalExchange } from "./exchanges";
|
|
3
3
|
import { isRoundTheClockCoin } from "./crypto-pair";
|
|
4
|
-
import { isCurrencyPairListing, latestFxWeekClose } from "./fx-market-hours";
|
|
5
|
-
import { hasPublishedSessionCalendar,
|
|
4
|
+
import { isCurrencyPairListing, isFxMarketOpen, latestFxWeekClose, latestFxWeekOpen } from "./fx-market-hours";
|
|
5
|
+
import { hasPublishedSessionCalendar, historySourceWeekendDays, latestRegularSessionClose, latestRegularSessionOpen, sessionCalendarTimeZone } from "../market-data/market/freshness";
|
|
6
6
|
import { zonedDateTimeParts } from "./zoned-date-time";
|
|
7
7
|
import { regularHistorySessionStaleness } from "../market-data/history-session";
|
|
8
8
|
import type { HistorySession } from "../types/price-history";
|
|
9
9
|
import { BAR_INTERVAL_UNIT_MS, parseBarInterval } from "../time-series/resolution";
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// A venue without session hours here (OTC Markets, Cboe Canada) has no
|
|
12
|
+
// session to hold: its copy is current while the latest bar is this recent.
|
|
13
|
+
const UNSCHEDULED_HISTORY_MAX_AGE_MS = 18 * 60 * 60 * 1000;
|
|
12
14
|
const MAX_SAME_SESSION_HISTORY_LAG_MS = 30 * 60 * 1000;
|
|
15
|
+
// Sources finish the closing auction and late prints some minutes after the
|
|
16
|
+
// bell, past the delayed feed's lag. A copy fetched later can still carry an
|
|
17
|
+
// unfinished bar when its source had not settled it; only the source can tell.
|
|
18
|
+
const SESSION_BAR_SETTLE_MS = 30 * 60 * 1000;
|
|
13
19
|
const DELAYED_HISTORY_ALLOWANCE_MS = 15 * 60 * 1000;
|
|
14
20
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
15
21
|
// A series that never closes must reach the present without a break: no hole
|
|
@@ -22,12 +28,19 @@ interface PriceHistoryFreshnessOptions {
|
|
|
22
28
|
exchange?: string;
|
|
23
29
|
/**
|
|
24
30
|
* The listing's symbol. A coin quoted in dollars (BTC-USD) trades around the
|
|
25
|
-
* clock whether or not the exchange says CCC
|
|
26
|
-
* pair is read as a US listing and
|
|
31
|
+
* clock whether or not the exchange says CCC, and a currency pair (EURUSD=X)
|
|
32
|
+
* on the FX week; without the symbol a bare pair is read as a US listing and
|
|
33
|
+
* judged on the NYSE session.
|
|
27
34
|
*/
|
|
28
35
|
symbol?: string;
|
|
29
36
|
intervalMs?: number | null;
|
|
30
37
|
session?: HistorySession;
|
|
38
|
+
/**
|
|
39
|
+
* When the copy was fetched. A copy fetched once its session had settled
|
|
40
|
+
* holds that session however early its last bar: a thin listing need not
|
|
41
|
+
* trade late. Without it, the bars must reach the close.
|
|
42
|
+
*/
|
|
43
|
+
fetchedAt?: number;
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
/**
|
|
@@ -217,38 +230,84 @@ export function isPriceHistoryStaleForCurrentWindow(
|
|
|
217
230
|
const age = now - latestTime;
|
|
218
231
|
const allowedLag = allowedBarLagMs(intervalMs);
|
|
219
232
|
if (age <= allowedLag) return false;
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
233
|
+
const sessions = listingSessionsAt(now, options);
|
|
234
|
+
if (!sessions) return age > UNSCHEDULED_HISTORY_MAX_AGE_MS;
|
|
235
|
+
const fetchedAt = options.fetchedAt ?? Number.NEGATIVE_INFINITY;
|
|
236
|
+
if (holdsLatestSession(latestTime, now, sessions, allowedLag, fetchedAt)) return false;
|
|
237
|
+
// A venue whose chart source files another week (DFM's Sunday to Thursday)
|
|
238
|
+
// may end on the last session that week has: current under either week.
|
|
239
|
+
// Bars of a day that week lacks (a DFM Friday) answer to the venue's own.
|
|
240
|
+
const exchange = listingSessionExchange(options.exchange);
|
|
241
|
+
const sourceWeekendDays = historySourceWeekendDays(exchange);
|
|
242
|
+
const timeZone = sessionCalendarTimeZone(exchange);
|
|
243
|
+
const latestWeekday = timeZone ? zonedWeekday(latestTime, timeZone) : null;
|
|
244
|
+
if (!sourceWeekendDays || latestWeekday == null || sourceWeekendDays.includes(latestWeekday)) return true;
|
|
245
|
+
const sourceSessions = listingSessionsAt(now, options, sourceWeekendDays);
|
|
246
|
+
return !sourceSessions || !holdsLatestSession(latestTime, now, sourceSessions, allowedLag, fetchedAt);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Bare symbols resolve to their US listing at the sources. */
|
|
250
|
+
function listingSessionExchange(exchange: string | undefined): string {
|
|
251
|
+
return canonicalExchange(exchange) || "NYSE";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
interface ListingSessions {
|
|
255
|
+
/** The open of the session trading at the time asked, if one is. */
|
|
256
|
+
liveOpen: number | null;
|
|
257
|
+
/** The latest session closed at or before that time. */
|
|
258
|
+
last: { open: number; close: number } | null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The sessions a listing's bars are read against at `time`, from the venue's
|
|
263
|
+
* session tables; `weekendDays` replaces its days off. A currency pair's
|
|
264
|
+
* session is the FX week, Sunday to Friday 17:00 New York, whatever venue it
|
|
265
|
+
* is quoted on. Null for a venue without session hours.
|
|
266
|
+
*/
|
|
267
|
+
function listingSessionsAt(
|
|
268
|
+
time: number,
|
|
269
|
+
options: Pick<PriceHistoryFreshnessOptions, "exchange" | "symbol">,
|
|
270
|
+
weekendDays?: readonly number[],
|
|
271
|
+
): ListingSessions | null {
|
|
272
|
+
if (isCurrencyPairListing(options.symbol, options.exchange)) {
|
|
273
|
+
const close = latestFxWeekClose(time);
|
|
274
|
+
const open = close === null ? null : latestFxWeekOpen(close);
|
|
275
|
+
return { liveOpen: isFxMarketOpen(time) ? latestFxWeekOpen(time) : null,
|
|
276
|
+
last: close !== null && open !== null ? { open, close } : null };
|
|
242
277
|
}
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
return
|
|
278
|
+
const exchange = listingSessionExchange(options.exchange);
|
|
279
|
+
const open = latestRegularSessionOpen(exchange, time, weekendDays);
|
|
280
|
+
if (open === null) return null;
|
|
281
|
+
const latest = latestRegularSessionClose(exchange, time, weekendDays);
|
|
282
|
+
const last = latest
|
|
283
|
+
? { open: latestRegularSessionOpen(exchange, latest.close, weekendDays) ?? latest.close, close: latest.close }
|
|
284
|
+
: null;
|
|
285
|
+
return { liveOpen: !last || last.close < open ? open : null, last };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Whether bars ending at `latestTime`, older than one bar's lag, hold the
|
|
290
|
+
* listing's latest session. Between sessions that is the whole of the last
|
|
291
|
+
* one. While one trades, bars older than the lag are behind it, and the whole
|
|
292
|
+
* of the one before answers only until this one's first delayed bar is due.
|
|
293
|
+
* A session is held whole by bars from its open that reach its close less
|
|
294
|
+
* that lag, or by a copy fetched once it had settled. Weeknights, weekends
|
|
295
|
+
* and holidays read alike: the session tables say which session is the last.
|
|
296
|
+
*/
|
|
297
|
+
function holdsLatestSession(
|
|
298
|
+
latestTime: number, now: number, sessions: ListingSessions, allowedLag: number, fetchedAt: number,
|
|
299
|
+
): boolean {
|
|
300
|
+
const { liveOpen, last } = sessions;
|
|
301
|
+
if (liveOpen !== null && (latestTime >= liveOpen || now - liveOpen > allowedLag)) return false;
|
|
302
|
+
return !!last && latestTime >= last.open
|
|
303
|
+
&& (latestTime >= last.close - allowedLag || fetchedAt >= last.close + SESSION_BAR_SETTLE_MS);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function zonedWeekday(time: number, timeZone: string): number {
|
|
307
|
+
const { year, month, day } = zonedDateTimeParts(time, timeZone);
|
|
308
|
+
return new Date(Date.UTC(year, month - 1, day)).getUTCDay();
|
|
246
309
|
}
|
|
247
310
|
|
|
248
|
-
// Sources finish the closing auction and late prints some minutes after the
|
|
249
|
-
// bell, past the delayed feed's lag. A copy fetched later can still carry an
|
|
250
|
-
// unfinished bar when its source had not settled it; only the source can tell.
|
|
251
|
-
const SESSION_BAR_SETTLE_MS = 30 * 60 * 1000;
|
|
252
311
|
const CRYPTO_BAR_MAX_AGE_MS = 60 * 60 * 1000;
|
|
253
312
|
// A copy missing a settled session is asked again at most this often, and less
|
|
254
313
|
// often as that close recedes, so a halted, delisted or late listing costs a
|
|
@@ -323,18 +382,16 @@ function isBarBeforeSession(latestTime: number, session: string, intervalMs: num
|
|
|
323
382
|
* next has begun, a copy whose last bar stops short of the close by more than
|
|
324
383
|
* a current copy's bars may lag is refetched. A copy fetched after the
|
|
325
384
|
* session settled holds all of it, however early its last bar: a thin listing
|
|
326
|
-
* need not trade late.
|
|
327
|
-
*
|
|
328
|
-
*
|
|
385
|
+
* need not trade late. The sessions are those isPriceHistoryStaleForCurrentWindow
|
|
386
|
+
* reads (listingSessionsAt): a currency pair's is the FX week. Coins never
|
|
387
|
+
* close, and within the FX week neither do pairs:
|
|
388
|
+
* isPriceHistoryStaleForCurrentWindow holds those copies to the present.
|
|
329
389
|
*/
|
|
330
390
|
function intradayHistoryFetchState(
|
|
331
391
|
points: PricePoint[], fetchedAt: number, now: number, intervalMs: number, options: CalendarHistoryFetchOptions,
|
|
332
392
|
): CalendarHistoryFetchState {
|
|
333
393
|
if (isRoundTheClockCoin(options.symbol, options.exchange)) return "current";
|
|
334
|
-
const
|
|
335
|
-
// Bare symbols resolve to their US listing at the sources.
|
|
336
|
-
const close = isCurrencyPairListing(options.symbol, options.exchange) ? latestFxWeekClose(settledBy)
|
|
337
|
-
: latestRegularSessionClose(canonicalExchange(options.exchange) || "NYSE", settledBy)?.close ?? null;
|
|
394
|
+
const close = listingSessionsAt(now - SESSION_BAR_SETTLE_MS, options)?.last?.close ?? null;
|
|
338
395
|
if (close === null || fetchedAt >= close + SESSION_BAR_SETTLE_MS) return "current";
|
|
339
396
|
const latest = points.findLast(hasFiniteClose);
|
|
340
397
|
const latestTime = latest ? getPricePointTimestamp(latest) : Number.NaN;
|
|
@@ -98,7 +98,8 @@ function splitEvents(proofs: Proof[]): SplitEvent[] {
|
|
|
98
98
|
/**
|
|
99
99
|
* Share bases belong to source accessions, not filing dates. A split edge needs
|
|
100
100
|
* an explicit SEC split fact and unchanged same-period income with inverse EPS
|
|
101
|
-
* restatement. Repeated EPS AND diluted shares
|
|
101
|
+
* restatement. Repeated EPS AND diluted shares (at the coarser filing's
|
|
102
|
+
* rounding) connect otherwise equal bases;
|
|
102
103
|
* where a filing tags no diluted shares, repeated EPS AND income do, if no
|
|
103
104
|
* disclosed split could leave that EPS unchanged at cent rounding.
|
|
104
105
|
* No price jump, ticker exception, or chronological assumption establishes one.
|
|
@@ -131,23 +132,30 @@ export function createSecEpsBasisResolver(payload: unknown): (fact: Fact) => { v
|
|
|
131
132
|
graph.set(after, [...graph.get(after) ?? [], { node: before, factor: ratio, evidence }]);
|
|
132
133
|
graph.set(before, [...graph.get(before) ?? [], { node: after, factor: 1 / ratio, evidence }]);
|
|
133
134
|
};
|
|
134
|
-
const identical = new Map<string, Row>();
|
|
135
135
|
const periods = new Map<string, Row[]>();
|
|
136
136
|
for (const row of eps.values()) {
|
|
137
137
|
const period = `${row.start}:${row.end}`;
|
|
138
138
|
periods.set(period, [...periods.get(period) ?? [], row]);
|
|
139
|
-
const denominator = shares.get(key(row))?.val;
|
|
140
|
-
if (!row.val || !denominator || denominator <= 0) continue;
|
|
141
|
-
const identity = `${period}:${row.val}:${denominator}`;
|
|
142
|
-
const prior = identical.get(identity);
|
|
143
|
-
if (prior && prior.accn !== row.accn) connect(prior.accn, row.accn, 1);
|
|
144
|
-
else identical.set(identity, row);
|
|
145
139
|
}
|
|
140
|
+
const ratios = [...new Set(splits.map((split) => split.val!))];
|
|
141
|
+
// A filing rounds its share counts to one unit, the largest power of ten
|
|
142
|
+
// dividing every diluted count it reports (Tesla gave thousands until 2019
|
|
143
|
+
// and millions after). Two counts are one basis when the finer one, rounded
|
|
144
|
+
// to the coarser filing's unit, gives the coarser one, and every disclosed
|
|
145
|
+
// split would move a count by more than that unit. Counts at exact share
|
|
146
|
+
// precision must be equal.
|
|
147
|
+
const unit = new Map<string, number>();
|
|
148
|
+
for (const row of entries(payload, "WeightedAverageNumberOfDilutedSharesOutstanding", "shares")) {
|
|
149
|
+
let power = 1;
|
|
150
|
+
while (row.val! > 0 && row.val! % (power * 10) === 0) power *= 10;
|
|
151
|
+
unit.set(row.accn!, Math.min(unit.get(row.accn!) ?? power, power));
|
|
152
|
+
}
|
|
153
|
+
const sameShares = (a: number, b: number, coarser: number) => a === b || coarser > 1 && Math.abs(a - b) * 2 <= coarser
|
|
154
|
+
&& ratios.every((ratio) => Math.abs(a - a / ratio) > coarser && Math.abs(a - a * ratio) > coarser);
|
|
146
155
|
// Not every filer tags diluted shares (Alphabet before 2024). Where a filing
|
|
147
156
|
// tags none for the period, a repeated EPS and net income connect instead,
|
|
148
157
|
// when no disclosed split ratio could leave that EPS unchanged at cent
|
|
149
158
|
// rounding. Diluted shares that differ still keep the bases apart.
|
|
150
|
-
const ratios = [...new Set(splits.map((split) => split.val!))];
|
|
151
159
|
const telling = (value: number) => ratios.every((ratio) => Math.abs(value - value / ratio) > .005 + .005 / ratio
|
|
152
160
|
&& Math.abs(value - value * ratio) > .005 + .005 * ratio);
|
|
153
161
|
const tagged = new Set(entries(payload, "WeightedAverageNumberOfDilutedSharesOutstanding", "shares").map(key));
|
|
@@ -155,8 +163,13 @@ export function createSecEpsBasisResolver(payload: unknown): (fact: Fact) => { v
|
|
|
155
163
|
for (let i = 0; i < rows.length; i++) {
|
|
156
164
|
for (let j = i + 1; j < rows.length; j++) {
|
|
157
165
|
const [a, b] = [rows[i]!, rows[j]!];
|
|
158
|
-
if (a.accn === b.accn || a.val !== b.val || !a.val
|
|
159
|
-
|
|
166
|
+
if (a.accn === b.accn || a.val !== b.val || !a.val) continue;
|
|
167
|
+
const [sharesA, sharesB] = [shares.get(key(a))?.val, shares.get(key(b))?.val];
|
|
168
|
+
if (sharesA && sharesB && sharesA > 0 && sharesB > 0) {
|
|
169
|
+
if (sameShares(sharesA, sharesB, Math.max(unit.get(a.accn) ?? 1, unit.get(b.accn) ?? 1))) connect(a.accn, b.accn, 1);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (!telling(a.val) || tagged.has(key(a)) && tagged.has(key(b))) continue;
|
|
160
173
|
const repeated = income.get(key(a))?.val;
|
|
161
174
|
if (repeated !== undefined && repeated !== 0 && repeated === income.get(key(b))?.val) connect(a.accn, b.accn, 1);
|
|
162
175
|
}
|