gloomberb 0.18.2 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (954) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/patches/electrobun@1.18.1.patch +106 -5
  6. package/src/account/checkout-handoff.tsx +225 -0
  7. package/src/account/device-signin-dialog.tsx +2 -2
  8. package/src/account/device-signin.ts +2 -2
  9. package/src/account/upgrade-dialog.tsx +1 -1
  10. package/src/api-client/askg.ts +2 -2
  11. package/src/api-client/auth.ts +2 -2
  12. package/src/api-client/central-bank-rates.ts +6 -0
  13. package/src/api-client/collections.ts +5 -1
  14. package/src/api-client/cot.ts +2 -0
  15. package/src/api-client/crypto-etf-flows.ts +37 -0
  16. package/src/api-client/crypto-markets.ts +60 -0
  17. package/src/api-client/data.ts +99 -5
  18. package/src/api-client/doe.ts +15 -0
  19. package/src/api-client/equity-screener.ts +28 -0
  20. package/src/api-client/exchange-announcements.ts +38 -0
  21. package/src/api-client/fund-facts.ts +39 -0
  22. package/src/api-client/futures-curve.ts +9 -1
  23. package/src/api-client/hiring.ts +301 -0
  24. package/src/api-client/index.ts +24 -5
  25. package/src/api-client/market-discovery.ts +14 -0
  26. package/src/api-client/members.ts +6 -0
  27. package/src/api-client/mna.ts +6 -0
  28. package/src/api-client/paths.ts +18 -1
  29. package/src/api-client/request.ts +26 -7
  30. package/src/api-client/short-side-publications.ts +37 -0
  31. package/src/api-client/socket.ts +64 -4
  32. package/src/api-client/swap-curve.ts +35 -0
  33. package/src/api-client/telemetry.ts +17 -0
  34. package/src/api-client/themes.ts +7 -1
  35. package/src/api-client/types.ts +90 -2
  36. package/src/app/global-shortcuts.ts +1 -16
  37. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  38. package/src/app/runtime/desktop-deeplink.ts +5 -5
  39. package/src/app/runtime/plugin-bindings.ts +2 -2
  40. package/src/app/runtime/startup.ts +13 -0
  41. package/src/app/runtime/update.ts +28 -8
  42. package/src/app/star-prompt/status-line.tsx +2 -2
  43. package/src/architecture/core-builtin-allowlist.json +5 -2
  44. package/src/architecture/pane-tabs-allowlist.json +65 -0
  45. package/src/architecture/ui-literals-allowlist.json +3 -3
  46. package/src/architecture/ui-tokens-allowlist.json +1 -1
  47. package/src/brokers/broker-portfolio-sync.ts +37 -0
  48. package/src/brokers/directory.ts +11 -0
  49. package/src/brokers/logos.ts +3 -1
  50. package/src/brokers/signed-in/adapter.ts +304 -26
  51. package/src/brokers/signed-in/client.ts +14 -2
  52. package/src/brokers/signed-in/connect.ts +10 -2
  53. package/src/brokers/sync-broker-instance.ts +9 -1
  54. package/src/capabilities/chart-series.ts +22 -6
  55. package/src/cli/bank-section.ts +1 -1
  56. package/src/cli/base-converter.ts +23 -14
  57. package/src/cli/central-bank-events.ts +1 -1
  58. package/src/cli/command-options.ts +59 -4
  59. package/src/cli/commands/api.ts +9 -6
  60. package/src/cli/commands/broker.ts +2 -2
  61. package/src/cli/commands/command-utils.ts +2 -2
  62. package/src/cli/commands/crypto-hints.ts +7 -20
  63. package/src/cli/commands/fred-series.ts +4 -2
  64. package/src/cli/commands/keybindings.ts +2 -2
  65. package/src/cli/commands/market.ts +1189 -296
  66. package/src/cli/commands/news-hints.ts +2 -2
  67. package/src/cli/commands/news-search.ts +59 -6
  68. package/src/cli/commands/options-leaps.ts +41 -26
  69. package/src/cli/commands/overview.ts +121 -48
  70. package/src/cli/commands/plugins.ts +9 -9
  71. package/src/cli/commands/remote.ts +13 -10
  72. package/src/cli/commands/search.ts +64 -44
  73. package/src/cli/commands/system.ts +27 -19
  74. package/src/cli/commands/themes.ts +1 -1
  75. package/src/cli/commands/ticker.ts +272 -70
  76. package/src/cli/context.ts +3 -2
  77. package/src/cli/desktop-pane-shot-view.ts +2 -2
  78. package/src/cli/desktop-pane-shot.ts +1 -1
  79. package/src/cli/earnings-calendar.ts +4 -4
  80. package/src/cli/entry.ts +18 -1
  81. package/src/cli/errors.ts +20 -2
  82. package/src/cli/exit-codes.ts +1 -1
  83. package/src/cli/export-source.ts +10 -4
  84. package/src/cli/freshness.ts +2 -8
  85. package/src/cli/fx-forward.ts +130 -0
  86. package/src/cli/fx-pair.ts +10 -9
  87. package/src/cli/glossary.ts +64 -20
  88. package/src/cli/help.ts +20 -14
  89. package/src/cli/history-export.ts +126 -0
  90. package/src/cli/history-rows.ts +196 -43
  91. package/src/cli/index-roots.ts +5 -45
  92. package/src/cli/index.ts +32 -18
  93. package/src/cli/issuer-membership.ts +103 -0
  94. package/src/cli/known-gaps.ts +118 -25
  95. package/src/cli/listing-arg.ts +315 -43
  96. package/src/cli/name-entries.ts +73 -22
  97. package/src/cli/native-smoke.ts +2 -0
  98. package/src/cli/not-a-ticker.ts +19 -9
  99. package/src/cli/opentui-libc.ts +5 -0
  100. package/src/cli/options.ts +12 -0
  101. package/src/cli/pane-functions/capabilities.ts +77 -20
  102. package/src/cli/pane-functions/catalog.ts +40 -23
  103. package/src/cli/pane-functions/discovery.ts +1 -1
  104. package/src/cli/pane-functions/dom.ts +12 -8
  105. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  106. package/src/cli/pane-functions/freshness.ts +174 -23
  107. package/src/cli/pane-functions/function-help.ts +66 -27
  108. package/src/cli/pane-functions/headless.ts +48 -16
  109. package/src/cli/pane-functions/index.ts +82 -21
  110. package/src/cli/pane-functions/options.ts +22 -2
  111. package/src/cli/pane-functions/report-notices.ts +12 -4
  112. package/src/cli/pane-functions/report.ts +5 -5
  113. package/src/cli/pane-functions/resolver.ts +65 -14
  114. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  115. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  116. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  117. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  118. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  119. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  120. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  121. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  122. package/src/cli/pane-functions/shot-options.ts +8 -3
  123. package/src/cli/peso-dollar-rates.ts +79 -0
  124. package/src/cli/quote-answer.ts +38 -3
  125. package/src/cli/report-tables.ts +190 -27
  126. package/src/cli/restore-plugins.ts +29 -14
  127. package/src/cli/result.ts +122 -25
  128. package/src/cli/series-search.ts +5 -5
  129. package/src/cli/statement-filings.ts +19 -0
  130. package/src/cli/suffixed-listing-rows.ts +49 -0
  131. package/src/cli/symbol-args.ts +50 -15
  132. package/src/cli/symbol-cells.ts +120 -0
  133. package/src/cli/symbol-list.ts +226 -60
  134. package/src/cli/symbol-rows.ts +131 -25
  135. package/src/cli/ticker-resolution.ts +108 -11
  136. package/src/cli/treasury-yields.ts +5 -5
  137. package/src/cli/types.ts +2 -0
  138. package/src/cli/uncovered-instrument.ts +3 -3
  139. package/src/cli/unknown-command.ts +6 -6
  140. package/src/components/chart/composite/chart-layout.ts +131 -0
  141. package/src/components/chart/composite/composite-chart.tsx +387 -21
  142. package/src/components/chart/composite/drawing-store.ts +26 -6
  143. package/src/components/chart/composite/interactions.ts +9 -3
  144. package/src/components/chart/composite/legend.tsx +75 -3
  145. package/src/components/chart/composite/levels.ts +5 -0
  146. package/src/components/chart/composite/panel-gestures.ts +41 -0
  147. package/src/components/chart/composite/panel-labels.ts +2 -1
  148. package/src/components/chart/composite/panel-surface.tsx +316 -11
  149. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  150. package/src/components/chart/composite/rasterizer.ts +44 -19
  151. package/src/components/chart/composite/scene.ts +16 -4
  152. package/src/components/chart/composite/text-renderer.ts +48 -21
  153. package/src/components/chart/composite/toolbar.tsx +52 -5
  154. package/src/components/chart/composite/types.ts +19 -0
  155. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  156. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  157. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  158. package/src/components/chart/composite/value-scale.ts +149 -0
  159. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  160. package/src/components/chart/core/pointer.ts +2 -0
  161. package/src/components/chart-table/index.ts +2 -0
  162. package/src/components/chart-table/read.tsx +74 -0
  163. package/src/components/command-bar/commands/collection-list.ts +41 -19
  164. package/src/components/command-bar/commands/collection.ts +9 -3
  165. package/src/components/command-bar/commands/direct/index.ts +1 -1
  166. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  167. package/src/components/command-bar/pane-templates/items.ts +59 -6
  168. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  169. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  170. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  171. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  172. package/src/components/command-bar/routes/root/results.ts +18 -4
  173. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  174. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  175. package/src/components/command-bar/routes/root/selection.ts +6 -1
  176. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  177. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  178. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  179. package/src/components/command-bar/selection-runtime.ts +50 -10
  180. package/src/components/command-bar/surface/index.tsx +24 -2
  181. package/src/components/command-bar/view-model.ts +7 -0
  182. package/src/components/command-bar/workflow/broker.ts +2 -2
  183. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  184. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  185. package/src/components/command-bar/workflow/fields.ts +8 -2
  186. package/src/components/command-bar/workflow/ops.ts +18 -4
  187. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  188. package/src/components/command-bar/workflow/submit.ts +3 -2
  189. package/src/components/command-bar/workflow/tickers.ts +5 -1
  190. package/src/components/company-logo.tsx +39 -0
  191. package/src/components/data-table/sections.ts +4 -2
  192. package/src/components/data-table/view.tsx +23 -3
  193. package/src/components/form-modal/confirm.tsx +2 -1
  194. package/src/components/form-modal/request.ts +5 -0
  195. package/src/components/form-modal/routes.ts +4 -1
  196. package/src/components/index.ts +13 -6
  197. package/src/components/layout/detached-pane-shell.tsx +1 -0
  198. package/src/components/layout/floating-pane.tsx +1 -0
  199. package/src/components/layout/header.tsx +234 -60
  200. package/src/components/layout/market-summary.ts +9 -6
  201. package/src/components/layout/pane/capture-status.ts +1 -1
  202. package/src/components/layout/pane/footer/model.ts +11 -5
  203. package/src/components/layout/pane/header.tsx +10 -3
  204. package/src/components/layout/pane/index.tsx +1 -0
  205. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  206. package/src/components/layout/shell/chrome.ts +19 -6
  207. package/src/components/layout/shell/index.tsx +2 -1
  208. package/src/components/layout/shell/pane/layers.tsx +19 -1
  209. package/src/components/layout/status-bar.tsx +2 -2
  210. package/src/components/market-board/index.tsx +11 -3
  211. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  212. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  213. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  214. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  215. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  216. package/src/components/pane-settings-dialog/value.ts +8 -1
  217. package/src/components/pane-settings-dialog.tsx +44 -13
  218. package/src/components/sign-in-code-panel.tsx +27 -19
  219. package/src/components/ui/column-grid.tsx +48 -4
  220. package/src/components/ui/country-flag-art.tsx +180 -0
  221. package/src/components/ui/country-flag.tsx +118 -12
  222. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  223. package/src/components/ui/data-table/opentui/model.ts +109 -2
  224. package/src/components/ui/data-table/types.ts +1 -1
  225. package/src/components/ui/display.tsx +5 -1
  226. package/src/components/ui/index.ts +3 -1
  227. package/src/components/ui/logo-mark.tsx +11 -2
  228. package/src/components/ui/query-bar.tsx +1 -1
  229. package/src/components/ui/row-notes.ts +79 -0
  230. package/src/components/ui/stat-grid.tsx +28 -3
  231. package/src/components/ui/status.tsx +3 -1
  232. package/src/core/app-services.ts +4 -0
  233. package/src/core/state/app/layout.ts +4 -4
  234. package/src/data/cached-query.ts +8 -1
  235. package/src/data/config/chart-settings.ts +3 -3
  236. package/src/data/config/home.ts +10 -2
  237. package/src/data/config/layout.ts +4 -1
  238. package/src/data/config/store/node.ts +4 -0
  239. package/src/data/config/store/normalize.ts +13 -3
  240. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  241. package/src/data/json-storage.ts +2 -0
  242. package/src/data/saved-copies.ts +84 -0
  243. package/src/docs/snapshot.json +65 -32
  244. package/src/i18n/es.ts +51 -12
  245. package/src/i18n/ja.ts +51 -12
  246. package/src/i18n/ko.ts +51 -12
  247. package/src/i18n/zh-cn.ts +51 -12
  248. package/src/i18n/zh-tw.ts +51 -12
  249. package/src/layout/desks.ts +1 -1
  250. package/src/layout/launch-layout.ts +1 -1
  251. package/src/layout/pane-manager/floating-actions.ts +5 -3
  252. package/src/layout/pane-manager/floating.ts +43 -1
  253. package/src/layout/pane-manager/index.ts +2 -0
  254. package/src/layout/selected-ticker.ts +74 -0
  255. package/src/market-data/depositary-pair.ts +251 -0
  256. package/src/market-data/futures-board.ts +1 -1
  257. package/src/market-data/futures-contract.ts +124 -40
  258. package/src/market-data/fx-closes.ts +17 -2
  259. package/src/market-data/hooks.tsx +19 -11
  260. package/src/market-data/market/change-basis.ts +49 -7
  261. package/src/market-data/market/freshness.ts +34 -5
  262. package/src/market-data/market/period-return.ts +163 -0
  263. package/src/market-data/market/trading-sessions.ts +13 -0
  264. package/src/market-data/market/year-to-date.ts +8 -13
  265. package/src/market-data/performance.ts +20 -14
  266. package/src/market-data/perpetual-premium.ts +32 -0
  267. package/src/market-data/published-apac-sessions.ts +2 -1
  268. package/src/market-data/published-futures-sessions.ts +69 -0
  269. package/src/market-data/quotes/contributions.ts +5 -1
  270. package/src/market-data/quotes/freshness.ts +42 -0
  271. package/src/market-data/quotes/intraday-bar.ts +37 -0
  272. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  273. package/src/market-data/quotes/resolution.ts +4 -2
  274. package/src/market-data/tax-equivalent.ts +1 -1
  275. package/src/market-data/ticker-price-returns.ts +3 -1
  276. package/src/news/aggregator.ts +5 -1
  277. package/src/news/news-model.ts +4 -0
  278. package/src/news/types.ts +11 -0
  279. package/src/notifications/app-notifier.ts +1 -1
  280. package/src/plugins/auto-update.ts +2 -2
  281. package/src/plugins/builtin/account-management/index.tsx +1 -0
  282. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  283. package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
  284. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  285. package/src/plugins/builtin/alerts/cli.ts +485 -76
  286. package/src/plugins/builtin/alerts/command.ts +5 -10
  287. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  288. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  289. package/src/plugins/builtin/alerts/events.ts +108 -5
  290. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  291. package/src/plugins/builtin/alerts/format.ts +2 -1
  292. package/src/plugins/builtin/alerts/headless.ts +100 -39
  293. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  294. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  295. package/src/plugins/builtin/alerts/index.tsx +45 -18
  296. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  297. package/src/plugins/builtin/alerts/levels.ts +3 -3
  298. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  299. package/src/plugins/builtin/alerts/price.ts +35 -0
  300. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  301. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  302. package/src/plugins/builtin/alerts/research-rules.ts +77 -3
  303. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  304. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  305. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  306. package/src/plugins/builtin/alerts/storage.ts +13 -10
  307. package/src/plugins/builtin/alerts/targets.ts +12 -3
  308. package/src/plugins/builtin/analytics/index.tsx +8 -2
  309. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  310. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  311. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  312. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  313. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  314. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  315. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  316. package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
  317. package/src/plugins/builtin/attention/pane.tsx +22 -16
  318. package/src/plugins/builtin/awards/headless.ts +7 -3
  319. package/src/plugins/builtin/awards/model.ts +18 -0
  320. package/src/plugins/builtin/awards/pane.tsx +12 -9
  321. package/src/plugins/builtin/backtest/export.ts +1 -1
  322. package/src/plugins/builtin/backtest/headless.ts +3 -3
  323. package/src/plugins/builtin/backtest/rules.ts +41 -5
  324. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  325. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  326. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  327. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  328. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  329. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  330. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  331. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  332. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  333. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  334. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  335. package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
  336. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  337. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  338. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  339. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  340. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  341. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  342. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  343. package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
  344. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  345. package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
  346. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  347. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  348. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  349. package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
  350. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  351. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  352. package/src/plugins/builtin/catalysts/client.ts +2 -2
  353. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  354. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  355. package/src/plugins/builtin/catalysts/model.ts +49 -4
  356. package/src/plugins/builtin/catalysts/pane.tsx +9 -11
  357. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  358. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  359. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  360. package/src/plugins/builtin/changelog/index.tsx +1 -0
  361. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  362. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  363. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  364. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  365. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  366. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  367. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  368. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  369. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  370. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  371. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  372. package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
  373. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  374. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  375. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  376. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  377. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  378. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  379. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  380. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  381. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  382. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  383. package/src/plugins/builtin/chat/people.ts +1 -1
  384. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  385. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  386. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  387. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  388. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  390. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  391. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  392. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  393. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  394. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  395. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  396. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  397. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  398. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  399. package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
  400. package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
  401. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  402. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  403. package/src/plugins/builtin/company-attention/client.ts +117 -1
  404. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  405. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  406. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  407. package/src/plugins/builtin/company-attention/model.ts +33 -7
  408. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  409. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  410. package/src/plugins/builtin/company-attention/pane.tsx +89 -31
  411. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  412. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  413. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  414. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  415. package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
  416. package/src/plugins/builtin/composite-plugins.ts +2 -1
  417. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  418. package/src/plugins/builtin/connections/index.ts +1 -0
  419. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  420. package/src/plugins/builtin/correlation/headless.ts +7 -4
  421. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  422. package/src/plugins/builtin/correlation/settings.ts +1 -0
  423. package/src/plugins/builtin/cot/headless.ts +28 -6
  424. package/src/plugins/builtin/cot/model.ts +12 -0
  425. package/src/plugins/builtin/cot/pane.tsx +7 -5
  426. package/src/plugins/builtin/cpi/headless.ts +5 -1
  427. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  428. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  429. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  430. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  431. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  432. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  433. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  434. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  435. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  437. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  438. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  439. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  440. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  441. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  442. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  443. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  444. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  445. package/src/plugins/builtin/debug/index.tsx +1 -0
  446. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  447. package/src/plugins/builtin/docs/index.tsx +1 -1
  448. package/src/plugins/builtin/docs/pane.tsx +2 -2
  449. package/src/plugins/builtin/docs/targets.ts +1 -1
  450. package/src/plugins/builtin/doe/client.ts +5 -1
  451. package/src/plugins/builtin/doe/headless.ts +12 -6
  452. package/src/plugins/builtin/doe/model.ts +43 -6
  453. package/src/plugins/builtin/doe/pane.tsx +19 -12
  454. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  455. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  456. package/src/plugins/builtin/earnings/board.tsx +253 -50
  457. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  458. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  459. package/src/plugins/builtin/earnings/client.ts +4 -3
  460. package/src/plugins/builtin/earnings/headless.ts +53 -15
  461. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  462. package/src/plugins/builtin/earnings/index.tsx +15 -22
  463. package/src/plugins/builtin/earnings/model.ts +17 -12
  464. package/src/plugins/builtin/earnings/scope.ts +89 -0
  465. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  466. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  467. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  468. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  469. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  470. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  471. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  472. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  473. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  474. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  475. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  476. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  477. package/src/plugins/builtin/econ/index.tsx +112 -25
  478. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  479. package/src/plugins/builtin/econ/types.ts +32 -1
  480. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  481. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  482. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  483. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  484. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  485. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  486. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  487. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  488. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  489. package/src/plugins/builtin/equity-screener/headless.ts +77 -5
  490. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  491. package/src/plugins/builtin/equity-screener/model.ts +34 -0
  492. package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
  493. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  494. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  495. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  496. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  497. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  498. package/src/plugins/builtin/exposure/headless.ts +51 -6
  499. package/src/plugins/builtin/exposure/index.tsx +28 -2
  500. package/src/plugins/builtin/exposure/model.ts +7 -0
  501. package/src/plugins/builtin/exposure/pane.tsx +16 -10
  502. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  503. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  504. package/src/plugins/builtin/futures/model.ts +10 -9
  505. package/src/plugins/builtin/futures/returns.ts +8 -36
  506. package/src/plugins/builtin/futures/table.tsx +1 -1
  507. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  508. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  509. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  510. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  511. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  512. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  513. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  514. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  515. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  516. package/src/plugins/builtin/gpu/board.tsx +4 -1
  517. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  518. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  519. package/src/plugins/builtin/gpu/headless.ts +9 -4
  520. package/src/plugins/builtin/gpu/history.tsx +4 -2
  521. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  522. package/src/plugins/builtin/help/index.tsx +13 -3
  523. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  524. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  525. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  526. package/src/plugins/builtin/holders/client.ts +15 -2
  527. package/src/plugins/builtin/holders/format.ts +28 -17
  528. package/src/plugins/builtin/holders/headless.ts +48 -8
  529. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  530. package/src/plugins/builtin/holders/pane.tsx +79 -15
  531. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  532. package/src/plugins/builtin/holders/table-model.ts +2 -1
  533. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  534. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  535. package/src/plugins/builtin/insider/base-values.ts +11 -7
  536. package/src/plugins/builtin/insider/client.ts +3 -0
  537. package/src/plugins/builtin/insider/headless.ts +20 -7
  538. package/src/plugins/builtin/insider/index.tsx +17 -8
  539. package/src/plugins/builtin/insider/issuer.ts +34 -4
  540. package/src/plugins/builtin/insider/signals-client.ts +3 -3
  541. package/src/plugins/builtin/insider/signals.tsx +1 -1
  542. package/src/plugins/builtin/insider/table-model.ts +35 -13
  543. package/src/plugins/builtin/insider/table.tsx +5 -2
  544. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  545. package/src/plugins/builtin/iv-history/client.ts +20 -0
  546. package/src/plugins/builtin/iv-history/events.ts +145 -0
  547. package/src/plugins/builtin/iv-history/format.ts +3 -0
  548. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  549. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  550. package/src/plugins/builtin/iv-history/model.ts +13 -4
  551. package/src/plugins/builtin/iv-history/series.ts +3 -1
  552. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  553. package/src/plugins/builtin/jobs/model.ts +4 -15
  554. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  555. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  556. package/src/plugins/builtin/jodi/pane.tsx +14 -8
  557. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  558. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  559. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  560. package/src/plugins/builtin/macro-day/model.ts +29 -8
  561. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  562. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  563. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  564. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  565. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  566. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  567. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  568. package/src/plugins/builtin/market-movers/client.ts +36 -21
  569. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  570. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  571. package/src/plugins/builtin/market-movers/screener.ts +33 -11
  572. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  573. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  574. package/src/plugins/builtin/market-movers/table.ts +4 -1
  575. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  576. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  577. package/src/plugins/builtin/members/headless.ts +6 -5
  578. package/src/plugins/builtin/members/pane.tsx +11 -4
  579. package/src/plugins/builtin/mna/client.ts +1 -0
  580. package/src/plugins/builtin/mna/detail.tsx +20 -32
  581. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  582. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  583. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  584. package/src/plugins/builtin/mna/headless.ts +6 -13
  585. package/src/plugins/builtin/mna/model.ts +93 -15
  586. package/src/plugins/builtin/mna/pane.tsx +5 -4
  587. package/src/plugins/builtin/news/headless.ts +12 -6
  588. package/src/plugins/builtin/news/index.tsx +10 -1
  589. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  590. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  591. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  592. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  593. package/src/plugins/builtin/notes/cli.ts +50 -15
  594. package/src/plugins/builtin/notes/export.ts +17 -6
  595. package/src/plugins/builtin/notes/files.ts +16 -5
  596. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  597. package/src/plugins/builtin/options/carry.ts +2 -2
  598. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  599. package/src/plugins/builtin/options/index.tsx +1 -1
  600. package/src/plugins/builtin/options/model-spot.ts +70 -4
  601. package/src/plugins/builtin/options/view.tsx +51 -23
  602. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  603. package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
  604. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  605. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  606. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  607. package/src/plugins/builtin/options-scenario/client.ts +142 -29
  608. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  609. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  610. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  611. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  612. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  613. package/src/plugins/builtin/options-scenario/headless.ts +49 -21
  614. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  615. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  616. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  617. package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
  618. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  619. package/src/plugins/builtin/perps/freshness.ts +8 -5
  620. package/src/plugins/builtin/perps/headless.ts +3 -1
  621. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  622. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  623. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  624. package/src/plugins/builtin/perps/pane.tsx +36 -17
  625. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  626. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  627. package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
  628. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
  629. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  630. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  631. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  632. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  633. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  634. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  635. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  636. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  637. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  638. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  639. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  640. package/src/plugins/builtin/power/client.ts +6 -3
  641. package/src/plugins/builtin/power/evidence.ts +22 -7
  642. package/src/plugins/builtin/power/gas.tsx +5 -1
  643. package/src/plugins/builtin/power/pane.tsx +44 -32
  644. package/src/plugins/builtin/power/prices.tsx +10 -3
  645. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  646. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  647. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  648. package/src/plugins/builtin/research/event-model.ts +3 -1
  649. package/src/plugins/builtin/research/event-sources.ts +12 -4
  650. package/src/plugins/builtin/research/index.tsx +2 -1
  651. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  652. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  653. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  654. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  655. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  656. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  657. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  658. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  659. package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
  660. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  661. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  662. package/src/plugins/builtin/seasonality/client.ts +2 -1
  663. package/src/plugins/builtin/sec/forms.ts +13 -0
  664. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  665. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  666. package/src/plugins/builtin/sec/headless.ts +85 -8
  667. package/src/plugins/builtin/sec/index.tsx +58 -12
  668. package/src/plugins/builtin/sectors/client.ts +6 -4
  669. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  670. package/src/plugins/builtin/series-inventory.ts +2 -0
  671. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  672. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  673. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  674. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  675. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  676. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  677. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  678. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  679. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  680. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  681. package/src/plugins/builtin/shared/research-trading.tsx +23 -0
  682. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  683. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  684. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  685. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  686. package/src/plugins/builtin/short-interest/client.ts +2 -0
  687. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  688. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  689. package/src/plugins/builtin/short-interest/model.ts +70 -19
  690. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  691. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  692. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  693. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  694. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  695. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  696. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  697. package/src/plugins/builtin/short-interest/types.ts +9 -0
  698. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  699. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  700. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  701. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  702. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  703. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  704. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  705. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  706. package/src/plugins/builtin/themes/headless.ts +8 -5
  707. package/src/plugins/builtin/themes/model.ts +14 -4
  708. package/src/plugins/builtin/themes/pane.tsx +24 -5
  709. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  710. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  711. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  712. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  713. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  714. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  715. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  716. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  717. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  718. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  719. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  720. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  721. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  722. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  723. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  724. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  725. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  726. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  727. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  728. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  729. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  730. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  731. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  732. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  733. package/src/plugins/builtin/vol-surface/client.ts +28 -7
  734. package/src/plugins/builtin/vol-surface/headless.ts +43 -15
  735. package/src/plugins/builtin/vol-surface/model.ts +34 -12
  736. package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
  737. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  738. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  739. package/src/plugins/builtin/volatility/model.ts +5 -10
  740. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  741. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  742. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  743. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  744. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  745. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  746. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  747. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  748. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  749. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  750. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  751. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  752. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  753. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  754. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  755. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  756. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  757. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  758. package/src/plugins/bundle.ts +1 -1
  759. package/src/plugins/catalog-browser.ts +2 -1
  760. package/src/plugins/compat.ts +3 -3
  761. package/src/plugins/dependencies.ts +75 -14
  762. package/src/plugins/extracted-plugins.ts +132 -34
  763. package/src/plugins/host-link.ts +149 -34
  764. package/src/plugins/installer.ts +35 -10
  765. package/src/plugins/loader.ts +206 -38
  766. package/src/plugins/manager-node.ts +2 -2
  767. package/src/plugins/marketplace/deactivation.ts +22 -0
  768. package/src/plugins/marketplace/model.ts +2 -2
  769. package/src/plugins/marketplace/packs.ts +1 -1
  770. package/src/plugins/ownership.ts +1 -1
  771. package/src/plugins/peer-retry.ts +155 -0
  772. package/src/plugins/registry/contributions.ts +3 -1
  773. package/src/plugins/registry/index.ts +4 -3
  774. package/src/plugins/registry/plugin-state.ts +3 -0
  775. package/src/plugins/seed-notice.ts +20 -0
  776. package/src/plugins/shortcut-argument.ts +39 -0
  777. package/src/portfolio/allocation.ts +1 -1
  778. package/src/portfolio/collection-name.ts +31 -0
  779. package/src/portfolio/column-values.ts +10 -8
  780. package/src/portfolio/cost-fx.ts +296 -19
  781. package/src/portfolio/manual-lots.ts +75 -0
  782. package/src/portfolio/mutations.ts +60 -22
  783. package/src/portfolio/suffixed-listings.ts +209 -0
  784. package/src/portfolio/totals-currency.ts +24 -0
  785. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  786. package/src/public/time-series.ts +11 -8
  787. package/src/remote/schema.ts +1 -1
  788. package/src/renderers/browser/config-host.ts +3 -0
  789. package/src/renderers/browser/main.tsx +23 -3
  790. package/src/renderers/dom/boot-frame-page.ts +187 -0
  791. package/src/renderers/dom/boot-frame.ts +237 -0
  792. package/src/renderers/dom/build-assets.ts +46 -8
  793. package/src/renderers/dom/desktop/controls.tsx +1 -1
  794. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  795. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  796. package/src/renderers/dom/host/ascii-text.ts +16 -2
  797. package/src/renderers/dom/host/box.tsx +43 -2
  798. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  799. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  800. package/src/renderers/dom/host/image-surface.tsx +49 -0
  801. package/src/renderers/dom/host/media-surface.tsx +24 -18
  802. package/src/renderers/dom/host/mouse.ts +30 -0
  803. package/src/renderers/dom/host/size-observer.ts +1 -1
  804. package/src/renderers/dom/host/style.ts +1 -1
  805. package/src/renderers/dom/host/tabs.tsx +16 -5
  806. package/src/renderers/dom/host/top-surface.ts +127 -0
  807. package/src/renderers/dom/styles.css +115 -0
  808. package/src/renderers/dom/test-utils.tsx +2 -1
  809. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  810. package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
  811. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  812. package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
  813. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  814. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  815. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  816. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  817. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  818. package/src/renderers/electrobun/bun/index.ts +11 -1
  819. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  820. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  821. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  822. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  823. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  824. package/src/renderers/electrobun/view/config-host.ts +3 -0
  825. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  826. package/src/renderers/electrobun/view/main.tsx +30 -4
  827. package/src/renderers/electrobun/view/news-source.ts +9 -7
  828. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
  829. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  830. package/src/renderers/opentui/browser-open.ts +88 -0
  831. package/src/renderers/opentui/host.tsx +5 -14
  832. package/src/renderers/opentui/image/loader.ts +3 -2
  833. package/src/renderers/opentui/native-smoke.ts +10 -1
  834. package/src/renderers/opentui/pane-image/index.ts +2 -1
  835. package/src/renderers/opentui/render-lib.ts +253 -0
  836. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  837. package/src/renderers/opentui/start.tsx +8 -1
  838. package/src/renderers/share/layout-view.tsx +2 -2
  839. package/src/renderers/share/main.tsx +4 -4
  840. package/src/renderers/share/view.tsx +1 -1
  841. package/src/series/resolve.ts +22 -6
  842. package/src/series/standalone.ts +30 -2
  843. package/src/series/types.ts +8 -1
  844. package/src/shares/api.ts +4 -1
  845. package/src/shares/pane.ts +1 -1
  846. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  847. package/src/sources/gloomberb-cloud/index.ts +39 -16
  848. package/src/sources/gloomberb-cloud/news.ts +39 -0
  849. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  850. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  851. package/src/sources/listing-symbols.ts +37 -0
  852. package/src/sources/provider-errors.ts +16 -2
  853. package/src/sources/provider-router/financial-routes.ts +21 -3
  854. package/src/sources/provider-router/financials.ts +15 -2
  855. package/src/sources/provider-router/history.ts +30 -8
  856. package/src/sources/provider-router/index.ts +16 -0
  857. package/src/sources/provider-router/primary.ts +1 -1
  858. package/src/state/app/bootstrap.ts +23 -0
  859. package/src/state/hooks/inline-tickers.ts +30 -5
  860. package/src/sync/controller.ts +1 -1
  861. package/src/sync/core-contributors.ts +46 -14
  862. package/src/team/collections-sync.ts +2 -1
  863. package/src/team/collections.ts +178 -17
  864. package/src/telemetry/crash-reports.ts +21 -4
  865. package/src/telemetry/update-failures-node.ts +59 -0
  866. package/src/telemetry/update-failures.ts +307 -0
  867. package/src/test-support/builtin-panes.ts +1 -1
  868. package/src/theme/colors.ts +3 -30
  869. package/src/theme/ui-tokens.ts +37 -1
  870. package/src/tickers/company-name-listing.ts +215 -36
  871. package/src/tickers/company-name.ts +43 -0
  872. package/src/tickers/index-roots.ts +47 -0
  873. package/src/tickers/instrument-label.ts +5 -1
  874. package/src/tickers/listing-fate.ts +30 -0
  875. package/src/tickers/metadata.ts +10 -0
  876. package/src/tickers/perpetual-listing.ts +5 -2
  877. package/src/tickers/search/home-listing.ts +75 -0
  878. package/src/tickers/search/index.ts +76 -27
  879. package/src/tickers/search/ranking.ts +54 -4
  880. package/src/tickers/search/upsert.ts +29 -0
  881. package/src/tickers/terminal-codes.ts +416 -40
  882. package/src/time-series/bar-aggregation.ts +243 -0
  883. package/src/time-series/base-series.ts +307 -0
  884. package/src/time-series/chart-bounds.ts +349 -0
  885. package/src/time-series/chart-seed.ts +142 -0
  886. package/src/time-series/economic.ts +5 -0
  887. package/src/time-series/fundamentals.ts +18 -3
  888. package/src/time-series/history-acquisition.ts +499 -0
  889. package/src/time-series/leverage.ts +100 -0
  890. package/src/time-series/market-resolution.ts +4 -2
  891. package/src/time-series/market.ts +2 -1
  892. package/src/time-series/price-comparison.ts +175 -30
  893. package/src/time-series/range.ts +9 -1
  894. package/src/time-series/resolution.ts +174 -16
  895. package/src/time-series/resolve-context.ts +157 -0
  896. package/src/time-series/resolve.ts +126 -1577
  897. package/src/time-series/series-name.ts +38 -0
  898. package/src/time-series/series-presentation.ts +208 -0
  899. package/src/time-series/spec.ts +11 -6
  900. package/src/time-series/studies.ts +109 -12
  901. package/src/time-series/tail-reconcile.ts +121 -0
  902. package/src/time-series/types.ts +29 -2
  903. package/src/time-series/use-chart-resolution.ts +4 -5
  904. package/src/types/broker.ts +10 -0
  905. package/src/types/config.ts +14 -0
  906. package/src/types/data-provider.ts +20 -0
  907. package/src/types/exchange-rate.ts +6 -0
  908. package/src/types/financials.ts +104 -7
  909. package/src/types/headless.ts +83 -6
  910. package/src/types/instrument.ts +3 -0
  911. package/src/types/issuer-listings.ts +28 -0
  912. package/src/types/plugin.ts +65 -8
  913. package/src/types/price-history.ts +2 -0
  914. package/src/types/ticker.ts +13 -0
  915. package/src/types/trading.ts +59 -1
  916. package/src/ui/ascii-font.ts +13 -1
  917. package/src/ui/host.tsx +6 -1
  918. package/src/updater/command-names.ts +175 -0
  919. package/src/updater/failure.ts +163 -0
  920. package/src/updater/index.ts +97 -28
  921. package/src/utils/calendar-date.ts +32 -15
  922. package/src/utils/cash-runway.ts +241 -0
  923. package/src/utils/central-banks.ts +42 -0
  924. package/src/utils/cli-output.ts +259 -80
  925. package/src/utils/contract-month.ts +16 -0
  926. package/src/utils/crypto-pair.ts +61 -3
  927. package/src/utils/currency-pair.ts +26 -0
  928. package/src/utils/currency-units.ts +29 -0
  929. package/src/utils/datetime-format.ts +10 -0
  930. package/src/utils/debug-log.ts +1 -1
  931. package/src/utils/decimal-comma.ts +39 -6
  932. package/src/utils/exchange-announcements.ts +71 -0
  933. package/src/utils/exchange-rate-snapshot.ts +10 -0
  934. package/src/utils/financial-statements.ts +83 -4
  935. package/src/utils/format.ts +34 -16
  936. package/src/utils/fundamentals.ts +150 -34
  937. package/src/utils/fx-market-hours.ts +10 -1
  938. package/src/utils/latest-financial-period.ts +8 -0
  939. package/src/utils/linux-libc.ts +54 -0
  940. package/src/utils/market-capitalization.ts +111 -15
  941. package/src/utils/max-drawdown.ts +64 -0
  942. package/src/utils/network-failure.ts +83 -0
  943. package/src/utils/option-expiry.ts +139 -7
  944. package/src/utils/price-earnings.ts +11 -5
  945. package/src/utils/price-history.ts +18 -8
  946. package/src/utils/price-window.ts +131 -0
  947. package/src/utils/release-date.ts +47 -0
  948. package/src/utils/sec.ts +71 -0
  949. package/src/utils/throttled-fetch.ts +11 -4
  950. package/src/utils/trailing-eps.ts +67 -0
  951. package/src/utils/typed-text.ts +21 -0
  952. package/src/utils/utc-time.ts +24 -1
  953. package/src/version.ts +6 -0
  954. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -0,0 +1,38 @@
1
+ import { publicTickerKey } from "../utils/exchanges";
2
+ import type { ChartSeriesSpec, ChartStudySpec } from "./types";
3
+
4
+ /**
5
+ * What a series' errors are prefixed with: its label, else what was asked for
6
+ * (`VIX`, `FRED GDP`), never the internal id a preset gave it (`vix-market-close-3`).
7
+ */
8
+ export function seriesMessageName(spec: Pick<ChartSeriesSpec, "label" | "source">): string {
9
+ const label = spec.label?.trim();
10
+ if (label) return label;
11
+ switch (spec.source.kind) {
12
+ case "security": return publicTickerKey(spec.source.instrument.symbol, spec.source.instrument.exchange);
13
+ case "economic": return `FRED ${spec.source.seriesId}`;
14
+ case "capability": return spec.source.seriesId;
15
+ }
16
+ }
17
+
18
+ const STUDY_MESSAGE_NAMES: Partial<Record<ChartStudySpec["kind"], string>> = {
19
+ "realized-vol": "Realized volatility",
20
+ "volume-profile": "Volume profile",
21
+ "anchored-vwap": "Anchored VWAP",
22
+ "leverage-decay": "Leverage decay",
23
+ atr: "ATR",
24
+ sma: "SMA",
25
+ ema: "EMA",
26
+ rsi: "RSI",
27
+ macd: "MACD",
28
+ vwap: "VWAP",
29
+ };
30
+
31
+ /**
32
+ * What a study's errors and warnings are prefixed with: what it is
33
+ * (`Ratio`, `Realized volatility`), never the id the chart gave it
34
+ * (`pair:ratio`, `builtin:realized-vol:price`).
35
+ */
36
+ export function studyMessageName(spec: Pick<ChartStudySpec, "kind">): string {
37
+ return STUDY_MESSAGE_NAMES[spec.kind] ?? `${spec.kind[0]!.toUpperCase()}${spec.kind.slice(1)}`;
38
+ }
@@ -0,0 +1,208 @@
1
+ import { limitSeriesObservations } from "./financial-period-coverage";
2
+ import type { ManualChartResolution } from "./resolution";
3
+ import { getTimeSeriesField } from "./field-catalog";
4
+ import { collectPriceHistoryIntegrity } from "./market";
5
+ import { activeStudyInputSeriesIds } from "./studies";
6
+ import { applyResolvedSeriesTransform } from "./transforms";
7
+ import { clipSeriesToWindow } from "./alignment";
8
+ import type { StudyAnchor } from "./leverage";
9
+ import {
10
+ comparedBufferPoints,
11
+ priceComparisonBoundsForSeries,
12
+ priceObservationWindowFilter,
13
+ type PriceComparison,
14
+ } from "./price-comparison";
15
+ import { parsePublicTickerKey, publicTickerKey } from "../utils/exchanges";
16
+ import type {
17
+ ChartSeriesSpec,
18
+ ChartSpec,
19
+ ResolvedSeries,
20
+ } from "./types";
21
+ import { type DateBounds, filterPoints } from "./chart-bounds";
22
+
23
+ export function resolvePriceHistoryIntegrity(
24
+ spec: ChartSpec,
25
+ requestedSeries: readonly ResolvedSeries[],
26
+ visibleSeries: readonly ResolvedSeries[],
27
+ bounds: DateBounds,
28
+ resolution: ManualChartResolution | "auto",
29
+ dateWindow: ChartSpec["viewport"]["dateWindow"] | null,
30
+ ) {
31
+ const filters = new Map(requestedSeries.map((entry) => [entry.id, priceObservationWindowFilter(entry, bounds, resolution, dateWindow)]));
32
+ const requestedIds = activeStudyInputSeriesIds(spec.studies);
33
+ spec.series.filter((entry) => entry.visible !== false).forEach((entry) => requestedIds.add(entry.id));
34
+ const selected = requestedSeries.filter((entry) => requestedIds.has(entry.id)).map((entry) => limitSeriesObservations(spec, {
35
+ ...entry, points: entry.points.filter((point) => filters.get(entry.id)!(point.date.getTime())),
36
+ }));
37
+ return [
38
+ ...collectPriceHistoryIntegrity(selected, "requested-observation", (id, time) => filters.get(id)!(time)),
39
+ ...collectPriceHistoryIntegrity(visibleSeries, "visible-calculation"),
40
+ ];
41
+ }
42
+
43
+ export function assignAxes(
44
+ series: ResolvedSeries[],
45
+ specs: readonly { id: string; axis: ChartSeriesSpec["axis"] }[],
46
+ warnings: string[],
47
+ ): ResolvedSeries[] {
48
+ const requested = new Map(specs.map((spec) => [spec.id, spec.axis] as const));
49
+ const groupsByPanel = new Map<string, Partial<Record<"left" | "right", string>>>();
50
+ return series.map((entry) => {
51
+ const groups = groupsByPanel.get(entry.panelId) ?? {};
52
+ groupsByPanel.set(entry.panelId, groups);
53
+ const preferred = requested.get(entry.id);
54
+ let axis: "left" | "right";
55
+ if (preferred === "left" || preferred === "right") {
56
+ axis = preferred;
57
+ } else if (groups.left === entry.unitGroup) {
58
+ axis = "left";
59
+ } else if (groups.right === entry.unitGroup) {
60
+ axis = "right";
61
+ } else if (!groups.left) {
62
+ axis = "left";
63
+ } else {
64
+ axis = "right";
65
+ }
66
+ if (groups[axis] && groups[axis] !== entry.unitGroup) {
67
+ warnings.push(`${entry.label} shares the ${axis} axis with a different unit; choose an explicit panel for independent scaling.`);
68
+ }
69
+ groups[axis] ??= entry.unitGroup;
70
+ return { ...entry, axis };
71
+ });
72
+ }
73
+
74
+ /**
75
+ * A series drawn in a ticker's price (a moving average of it, a band around
76
+ * it) takes the units of that ticker's price series when the chart has it, so
77
+ * the two share an axis and the line sits on the price instead of on a scale
78
+ * of its own.
79
+ */
80
+ export function adoptPriceUnits(series: ResolvedSeries[], specs: readonly ChartSeriesSpec[]): ResolvedSeries[] {
81
+ if (!series.some((entry) => entry.priceOf)) return series;
82
+ const specsById = new Map(specs.map((spec) => [spec.id, spec]));
83
+ const prices = series.filter((entry) => {
84
+ const source = specsById.get(entry.id)?.source;
85
+ return source?.kind === "security" && getTimeSeriesField(source.fieldId)?.unitGroup === "price"
86
+ && entry.transform === "raw";
87
+ });
88
+ if (prices.length === 0) return series;
89
+ return series.map((entry) => {
90
+ if (!entry.priceOf) return entry;
91
+ const wanted = parsePublicTickerKey(entry.priceOf.toUpperCase());
92
+ const key = publicTickerKey(wanted.symbol, wanted.exchange);
93
+ const named = (price: ResolvedSeries) => {
94
+ const source = specsById.get(price.id)?.source;
95
+ if (source?.kind !== "security") return false;
96
+ return publicTickerKey(source.instrument.symbol, source.instrument.exchange) === key
97
+ || (!wanted.exchange && source.instrument.symbol.toUpperCase() === wanted.symbol);
98
+ };
99
+ const price = prices.find((candidate) => named(candidate) && candidate.panelId === entry.panelId)
100
+ ?? prices.find(named);
101
+ return price && entry.transform === "raw"
102
+ ? { ...entry, unit: price.unit, unitGroup: price.unitGroup }
103
+ : entry;
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Where studies that read returns over the chart's period measure each series
109
+ * from and to: a compared leg's shared first and last session, else the
110
+ * window in view.
111
+ */
112
+ export function studyAnchors(
113
+ series: readonly ResolvedSeries[],
114
+ comparison: PriceComparison | null,
115
+ bounds: DateBounds,
116
+ ): Map<string, StudyAnchor> {
117
+ return new Map(series.map((entry) => [entry.id, priceComparisonBoundsForSeries(entry, comparison) ?? bounds]));
118
+ }
119
+
120
+ export function prepareBaseSeriesForStudies(
121
+ series: ResolvedSeries,
122
+ bounds: DateBounds,
123
+ clipToBounds = false,
124
+ fallbackBaselineBounds?: DateBounds,
125
+ ): ResolvedSeries {
126
+ const baselineTransform = series.transform === "percent" || series.transform === "index100";
127
+ let source = series;
128
+ if (clipToBounds && bounds.start !== null && bounds.end !== null) {
129
+ source = clipSeriesToWindow(series, new Date(bounds.start), new Date(bounds.end));
130
+ } else if (clipToBounds) {
131
+ source = { ...series, points: filterPoints(series.points, bounds) };
132
+ }
133
+ const baseline = baselineTransform
134
+ ? scalarBaseline(series, bounds)
135
+ ?? (fallbackBaselineBounds ? scalarBaseline(series, fallbackBaselineBounds) : null)
136
+ : null;
137
+ return applyResolvedSeriesTransform(
138
+ source,
139
+ source.transform,
140
+ baselineTransform ? { baseline } : undefined,
141
+ );
142
+ }
143
+
144
+ function scalarBaseline(series: ResolvedSeries, bounds: DateBounds): number | null {
145
+ const points = filterPoints(series.points, bounds);
146
+ for (const point of points) {
147
+ const value = typeof point.value === "number" && Number.isFinite(point.value)
148
+ ? point.value
149
+ : typeof point.close === "number" && Number.isFinite(point.close)
150
+ ? point.close
151
+ : null;
152
+ if (value !== null && value !== 0) return value;
153
+ }
154
+ return null;
155
+ }
156
+
157
+ function studyForOutput(
158
+ outputId: string,
159
+ studies: readonly ChartSpec["studies"][number][],
160
+ ): ChartSpec["studies"][number] | undefined {
161
+ return studies
162
+ .filter((study) => outputId === study.id || outputId.startsWith(`${study.id}:`))
163
+ .sort((left, right) => right.id.length - left.id.length)[0];
164
+ }
165
+
166
+ /** Price overlays read in the price's own terms, so they take its transform. */
167
+ function followsInputTransform(kind: ChartSpec["studies"][number]["kind"]): boolean {
168
+ return kind === "sma" || kind === "ema" || kind === "bollinger" || kind === "vwap" || kind === "anchored-vwap";
169
+ }
170
+
171
+ export function presentationBounds(
172
+ series: ResolvedSeries,
173
+ studies: readonly ChartSpec["studies"][number][],
174
+ comparison: PriceComparison | null,
175
+ fallback: DateBounds,
176
+ ): DateBounds {
177
+ const study = studyForOutput(series.id, studies);
178
+ const sourceId = study && followsInputTransform(study.kind)
179
+ ? study.inputSeriesIds[0] : series.id;
180
+ return (sourceId && comparison?.sourceBounds?.[sourceId]) || fallback;
181
+ }
182
+
183
+ export function applyStudyPresentationTransforms(
184
+ outputs: ResolvedSeries[],
185
+ studies: readonly ChartSpec["studies"][number][],
186
+ rawSeries: readonly ResolvedSeries[],
187
+ visibleBounds: DateBounds,
188
+ fallbackBaselineBounds?: DateBounds,
189
+ priceComparison?: PriceComparison | null,
190
+ view: DateBounds = visibleBounds,
191
+ ): ResolvedSeries[] {
192
+ const rawById = new Map(rawSeries.map((series) => [series.id, series] as const));
193
+ return outputs.map((output) => {
194
+ const study = studyForOutput(output.id, studies);
195
+ if (!study || !followsInputTransform(study.kind)) {
196
+ return output;
197
+ }
198
+ const input = rawById.get(study.inputSeriesIds[0] ?? "");
199
+ if (!input || input.transform === "raw") return output;
200
+ const baseline = input.transform === "percent" || input.transform === "index100"
201
+ ? scalarBaseline(input, priceComparisonBoundsForSeries(input, priceComparison ?? null) ?? visibleBounds)
202
+ ?? (fallbackBaselineBounds ? scalarBaseline(input, fallbackBaselineBounds) : null)
203
+ : undefined;
204
+ const transformed = applyResolvedSeriesTransform(output, input.transform, { baseline });
205
+ const inputBounds = priceComparison?.sourceBounds?.[input.id];
206
+ return inputBounds ? { ...transformed, points: comparedBufferPoints(transformed.points, inputBounds, view) } : transformed;
207
+ });
208
+ }
@@ -1,6 +1,6 @@
1
- import { CHART_RESOLUTIONS, TIME_RANGES, type ChartResolution, type TimeRange } from "./range";
1
+ import { TIME_RANGES, type ChartResolution, type TimeRange } from "./range";
2
2
  import { isRealizedVolatilityEstimator } from "../market-data/realized-volatility";
3
- import { getChartResolutionLabel } from "./resolution";
3
+ import { getChartResolutionLabel, isChartResolution } from "./resolution";
4
4
  import {
5
5
  canonicalTimeSeriesFieldId,
6
6
  getTimeSeriesField,
@@ -32,9 +32,9 @@ import {
32
32
  isValidChartSeriesId,
33
33
  } from "../capabilities/chart-series";
34
34
  import { recordOrNull } from "../utils/guards";
35
+ import { isLeverageFactor } from "./leverage";
35
36
 
36
37
  const RANGE_SET = new Set<TimeRange>(TIME_RANGES);
37
- const RESOLUTIONS = new Set<ChartResolution>(CHART_RESOLUTIONS);
38
38
  const PERIODS = new Set<SeriesPeriod>(["auto", "daily", "weekly", "monthly", "quarterly", "annual", "ttm"]);
39
39
  // `band` only stays on a capability series: a field's styles and an economic series' leave it out.
40
40
  const STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points", "candles", "ohlc", "hlc", "band"]);
@@ -58,6 +58,8 @@ const STUDIES = new Set<ChartStudyKind>([
58
58
  "spread",
59
59
  "correlation",
60
60
  "formula",
61
+ "leverage",
62
+ "leverage-decay",
61
63
  ]);
62
64
 
63
65
  /**
@@ -321,7 +323,7 @@ export function normalizeChartSpec(value: unknown, fallback: ChartSpec = DEFAULT
321
323
  range: RANGE_SET.has(viewport?.range as TimeRange)
322
324
  ? viewport!.range as TimeRange
323
325
  : fallbackCopy.viewport.range,
324
- resolution: RESOLUTIONS.has(viewport?.resolution as ChartResolution)
326
+ resolution: isChartResolution(viewport?.resolution)
325
327
  ? viewport!.resolution as ChartResolution
326
328
  : fallbackCopy.viewport.resolution,
327
329
  dateWindow: normalizedDateWindow(viewport?.dateWindow),
@@ -343,7 +345,7 @@ function issue(path: string, code: string, message: string): ChartSpecIssue {
343
345
  }
344
346
 
345
347
  function requiredInputCount(kind: ChartStudyKind): number {
346
- return kind === "ratio" || kind === "spread" || kind === "correlation" ? 2 : 1;
348
+ return kind === "ratio" || kind === "spread" || kind === "correlation" || kind === "leverage-decay" ? 2 : 1;
347
349
  }
348
350
 
349
351
  /** Whether a study has the inputs its kind takes: two for a pair, one or more for a formula, one for the rest. */
@@ -361,7 +363,7 @@ export function validateChartSpec(spec: ChartSpec): ChartSpecValidationResult {
361
363
  errors.push(issue("version", "unsupported-version", `Unsupported chart spec version ${String(spec.version)}.`));
362
364
  }
363
365
  if (!RANGE_SET.has(spec.viewport.range)) errors.push(issue("viewport.range", "invalid-range", "Invalid date range."));
364
- if (!RESOLUTIONS.has(spec.viewport.resolution)) {
366
+ if (!isChartResolution(spec.viewport.resolution)) {
365
367
  errors.push(issue("viewport.resolution", "invalid-resolution", "Invalid chart resolution."));
366
368
  }
367
369
  if (spec.viewport.dateWindow) {
@@ -544,6 +546,9 @@ export function validateChartSpec(spec: ChartSpec): ChartSpecValidationResult {
544
546
  const slow = study.parameters.slow ?? 26;
545
547
  if (typeof fast === "number" && typeof slow === "number" && fast >= slow) errors.push(issue(`${path}.parameters`, "invalid-macd-periods", "MACD fast period must be shorter than slow period."));
546
548
  }
549
+ if ((study.kind === "leverage" || study.kind === "leverage-decay") && !isLeverageFactor(study.parameters.factor)) {
550
+ errors.push(issue(`${path}.parameters.factor`, "invalid-factor", "A leverage factor is a nonzero multiple of at most 5, such as 3 or -3."));
551
+ }
547
552
  });
548
553
 
549
554
  if (spec.series.length === 0) warnings.push(issue("series", "empty-chart", "Chart has no data series."));
@@ -17,6 +17,8 @@ import type {
17
17
  TimeSeriesPoint,
18
18
  } from "./types";
19
19
  import { isFiniteNumber } from "../utils/guards";
20
+ import { studyMessageName } from "./series-name";
21
+ import { dailyLeverageGrowth, isLeverageFactor, leverageDecayPoints, leverageFactorLabel, leverageSymbol, sessionCloses, type StudyAnchor } from "./leverage";
20
22
  import { GALLONS_PER_BARREL, isPerBarrelUnit, isPerGallonUnit, perBarrelUnit, quantityIdentity, unitCurrencyToken } from "./price-units";
21
23
 
22
24
  export interface StudyResolutionResult {
@@ -45,7 +47,10 @@ const KIND_COLORS: Partial<Record<ChartStudyKind, string>> = {
45
47
  "anchored-vwap": "#b197fc",
46
48
  "volume-profile": "#adb5bd",
47
49
  atr: "#63e6be",
50
+ leverage: "#e599f7",
48
51
  };
52
+ /** The decay study's two lines: the fund against the daily line, the daily line against the period multiple. */
53
+ const DECAY_COLORS = ["#ffa94d", "#66d9e8"] as const;
49
54
  /**
50
55
  * One per anchor, so eight lines never repeat a colour. None is red or pink
51
56
  * (falling candles take the theme's negative), orange (VWAP), yellow (the
@@ -117,6 +122,7 @@ function outputSeries(
117
122
  nativeFrequency: options.nativeFrequency ?? input.nativeFrequency,
118
123
  ...(historyResolution !== undefined ? { historyResolution } : {}),
119
124
  ...(input.historyStart && historyResolution === input.historyResolution ? { historyStart: input.historyStart } : {}),
125
+ ...(input.barSize && historyResolution === input.historyResolution ? { barSize: input.barSize } : {}),
120
126
  dataShape: "scalar",
121
127
  style: options.style ?? "line",
122
128
  transform: "raw",
@@ -1084,7 +1090,70 @@ function resolveFormula(
1084
1090
  }
1085
1091
 
1086
1092
  function requiredInputs(kind: ChartStudyKind): number {
1087
- return kind === "ratio" || kind === "spread" || kind === "correlation" ? 2 : 1;
1093
+ return kind === "ratio" || kind === "spread" || kind === "correlation" || kind === "leverage-decay" ? 2 : 1;
1094
+ }
1095
+
1096
+ /** The objective's line and its decay compound session returns: they need daily bars, whatever the chart's range. */
1097
+ function dailyBarsIssue(spec: ChartStudySpec, input: ResolvedSeries, marketResolution: ManualChartResolution | undefined): string | null {
1098
+ const resolution = input.historyResolution === undefined ? marketResolution : input.historyResolution;
1099
+ if (input.nativeFrequency === "daily" && (resolution === undefined || resolution === "1d")) return null;
1100
+ return `${studyMessageName(spec)}: ${leverageFactorLabel(Number(spec.parameters.factor))} daily needs daily bars. Choose D bars.`;
1101
+ }
1102
+
1103
+ /**
1104
+ * The line a leveraged or inverse fund's daily objective draws from its index
1105
+ * fund, in percent from where the chart compares (3× daily QQQ beside TQQQ),
1106
+ * on the percent axis the compared legs share.
1107
+ */
1108
+ function resolveLeverageLine(spec: ChartStudySpec, index: ResolvedSeries, color: string, anchor: StudyAnchor | undefined): ResolvedSeries[] {
1109
+ const factor = spec.parameters.factor as number;
1110
+ const closes = sessionCloses(index, anchor);
1111
+ const growth = dailyLeverageGrowth(closes.map((entry) => entry.close), factor);
1112
+ return [outputSeries(spec, index, {
1113
+ label: `${leverageFactorLabel(factor)} daily ${leverageSymbol(index)}`,
1114
+ points: closes.map((entry, position) => derivedPoint({ point: entry.point, value: entry.close }, (growth[position]! - 1) * 100)),
1115
+ color,
1116
+ unit: "%",
1117
+ unitGroup: "percent",
1118
+ statedUnit: null,
1119
+ priceAssetCategory: null,
1120
+ axis: "left",
1121
+ })];
1122
+ }
1123
+
1124
+ /**
1125
+ * How far the fund falls short of its daily objective, in percentage points
1126
+ * from where the chart compares: the fund against the daily line (fees,
1127
+ * financing, tracking), and the daily line against the factor times the index
1128
+ * fund's return over the period (compounding, which daily resets cost in a
1129
+ * market that swings).
1130
+ */
1131
+ function resolveLeverageDecay(
1132
+ spec: ChartStudySpec,
1133
+ fund: ResolvedSeries,
1134
+ index: ResolvedSeries,
1135
+ anchors: ReadonlyMap<string, StudyAnchor> | undefined,
1136
+ ): ResolvedSeries[] {
1137
+ const factor = spec.parameters.factor as number;
1138
+ const decay = leverageDecayPoints(sessionCloses(fund, anchors?.get(fund.id)), sessionCloses(index, anchors?.get(index.id)), factor);
1139
+ const daily = `${leverageFactorLabel(factor)} daily`;
1140
+ const line = (id: string, label: string, color: string, value: (point: (typeof decay)[number]) => number) => outputSeries(spec, fund, {
1141
+ id: `${spec.id}:${id}`,
1142
+ label,
1143
+ points: decay.map((entry) => derivedPoint({ point: entry.point, value: entry.fund }, value(entry))),
1144
+ color: spec.color ?? color,
1145
+ unit: "pp",
1146
+ // Not a percent group: a difference of returns reads in points (-0.96 pp), not as a return.
1147
+ unitGroup: "pp",
1148
+ statedUnit: null,
1149
+ priceAssetCategory: null,
1150
+ axis: "left",
1151
+ });
1152
+ return [
1153
+ line("fund", `${leverageSymbol(fund)} decay vs ${daily} ${leverageSymbol(index)}`, DECAY_COLORS[0], (entry) => entry.fund - entry.daily),
1154
+ // `3× QQQ`: three times QQQ's return over the period, the multiple a daily reset does not deliver.
1155
+ line("compounding", `${daily} ${leverageSymbol(index)} decay vs ${leverageFactorLabel(factor)} ${leverageSymbol(index)}`, DECAY_COLORS[1], (entry) => entry.daily - entry.period),
1156
+ ];
1088
1157
  }
1089
1158
 
1090
1159
  /**
@@ -1188,16 +1257,23 @@ export function activeStudyInputSeriesIds(
1188
1257
  .flatMap((spec) => spec.inputSeriesIds));
1189
1258
  }
1190
1259
 
1260
+ /**
1261
+ * `anchors`: where each input series is measured from and to, for studies
1262
+ * that read returns over the chart's period (a leveraged fund's daily line and
1263
+ * its decay start where a comparison starts its legs). Without one, a series'
1264
+ * own first and last observations.
1265
+ */
1191
1266
  export function resolveStudies(
1192
1267
  baseSeries: readonly ResolvedSeries[],
1193
1268
  studySpecs: readonly ChartStudySpec[],
1194
1269
  marketResolution?: ManualChartResolution,
1195
1270
  historicalPriceSeries?: ReadonlyMap<string, ResolvedSeries>,
1271
+ anchors?: ReadonlyMap<string, StudyAnchor>,
1196
1272
  ): StudyResolutionResult {
1197
1273
  // With one security on the chart its volume is just "Volume"; beside a
1198
1274
  // comparison the legend names whose volume it is.
1199
1275
  const nameVolumeInput = baseSeries.filter((series) => series.observationKind === "market").length > 1;
1200
- return resolveStudySpecs(baseSeries, studySpecs, nameVolumeInput, marketResolution, historicalPriceSeries);
1276
+ return resolveStudySpecs(baseSeries, studySpecs, nameVolumeInput, marketResolution, historicalPriceSeries, anchors);
1201
1277
  }
1202
1278
 
1203
1279
  function resolveStudySpecs(
@@ -1206,6 +1282,7 @@ function resolveStudySpecs(
1206
1282
  nameVolumeInput: boolean,
1207
1283
  marketResolution?: ManualChartResolution,
1208
1284
  historicalPriceSeries?: ReadonlyMap<string, ResolvedSeries>,
1285
+ anchors?: ReadonlyMap<string, StudyAnchor>,
1209
1286
  ): StudyResolutionResult {
1210
1287
  const byId = new Map(baseSeries.map((series) => [series.id, series]));
1211
1288
  const resolved: ResolvedSeries[] = [];
@@ -1218,7 +1295,7 @@ function resolveStudySpecs(
1218
1295
  const inputs = spec.inputSeriesIds.map((id) => spec.kind === "realized-vol"
1219
1296
  ? historicalPriceSeries?.get(id) ?? byId.get(id) : byId.get(id));
1220
1297
  if (inputs.length !== required || inputs.some((input) => !input)) {
1221
- errors.push(`${spec.id}: ${spec.kind} requires ${required} valid input series.`);
1298
+ errors.push(`${studyMessageName(spec)}: ${spec.kind} requires ${required} valid input series.`);
1222
1299
  return;
1223
1300
  }
1224
1301
  const input = inputs[0]!;
@@ -1226,7 +1303,7 @@ function resolveStudySpecs(
1226
1303
  const pairedInput = inputs[1]!;
1227
1304
  const inputUnit = knownUnitSignature(input);
1228
1305
  if (inputUnit === null || inputUnit !== knownUnitSignature(pairedInput)) {
1229
- errors.push(`${spec.id}: ${unitMismatchMessage("spread", { series: input, unit: input.unit }, { series: pairedInput, unit: pairedInput.unit })}`);
1306
+ errors.push(`${studyMessageName(spec)}: ${unitMismatchMessage("spread", { series: input, unit: input.unit }, { series: pairedInput, unit: pairedInput.unit })}`);
1230
1307
  return;
1231
1308
  }
1232
1309
  }
@@ -1235,17 +1312,37 @@ function resolveStudySpecs(
1235
1312
  const read = formulaTerms(spec, inputs as ResolvedSeries[]);
1236
1313
  const mismatch = "error" in read ? read.error : formulaUnitMismatch(read, inputs as ResolvedSeries[]);
1237
1314
  if (mismatch) {
1238
- errors.push(`${spec.id}: ${mismatch}`);
1315
+ errors.push(`${studyMessageName(spec)}: ${mismatch}`);
1239
1316
  return;
1240
1317
  }
1241
1318
  terms = read as FormulaTerms;
1242
1319
  }
1243
1320
  const color = spec.color ?? KIND_COLORS[spec.kind] ?? STUDY_COLORS[index % STUDY_COLORS.length]!;
1321
+ if (spec.kind === "leverage" || spec.kind === "leverage-decay") {
1322
+ const index = inputs.at(-1)!;
1323
+ if (!isLeverageFactor(spec.parameters.factor)) {
1324
+ errors.push(`${studyMessageName(spec)}: choose a nonzero leverage factor of at most 5.`);
1325
+ return;
1326
+ }
1327
+ const issue = inputs.map((entry) => dailyBarsIssue(spec, entry!, marketResolution)).find(Boolean);
1328
+ if (issue) {
1329
+ warnings.push(issue);
1330
+ return;
1331
+ }
1332
+ const outputs = spec.kind === "leverage"
1333
+ ? resolveLeverageLine(spec, index, color, anchors?.get(index.id))
1334
+ : resolveLeverageDecay(spec, input, index, anchors);
1335
+ if (outputs.every((output) => output.points.length < 2)) {
1336
+ warnings.push(`${studyMessageName(spec)}: not enough shared daily closes to measure ${leverageFactorLabel(spec.parameters.factor as number)} daily ${leverageSymbol(index)}.`);
1337
+ }
1338
+ resolved.push(...outputs);
1339
+ return;
1340
+ }
1244
1341
  if (spec.kind === "realized-vol") {
1245
1342
  const window = spec.parameters.window ?? 30;
1246
1343
  if (typeof window !== "number" || !Number.isInteger(window) || window < 2
1247
1344
  || (spec.parameters.estimator !== undefined && !isRealizedVolatilityEstimator(spec.parameters.estimator))) {
1248
- errors.push(`${spec.id}: choose a supported volatility estimator and a whole window of at least two sessions.`);
1345
+ errors.push(`${studyMessageName(spec)}: choose a supported volatility estimator and a whole window of at least two sessions.`);
1249
1346
  return;
1250
1347
  }
1251
1348
  const inputResolution = input.historyResolution === undefined ? marketResolution : input.historyResolution;
@@ -1253,17 +1350,17 @@ function resolveStudySpecs(
1253
1350
  || (inputResolution !== undefined && inputResolution !== "1d")
1254
1351
  || (input.timeBasis?.cadenceMs !== undefined && input.timeBasis.cadenceMs !== 86_400_000)
1255
1352
  || !(input.unitGroup === "price" || input.unitGroup.startsWith("price:"))) {
1256
- errors.push(`${spec.id}: realized volatility requires daily prices. Choose Auto or 1D resolution and a daily price source.`);
1353
+ errors.push(`${studyMessageName(spec)}: realized volatility requires daily prices. Choose Auto or 1D resolution and a daily price source.`);
1257
1354
  return;
1258
1355
  }
1259
1356
  const cadenceIssue = realizedVolatilityCadenceIssue(input.points);
1260
1357
  if (cadenceIssue) {
1261
- errors.push(`${spec.id}: ${cadenceIssue}`);
1358
+ errors.push(`${studyMessageName(spec)}: ${cadenceIssue}`);
1262
1359
  return;
1263
1360
  }
1264
1361
  const outputs = resolveRealizedVolatility(spec, input, color);
1265
1362
  if (outputs.every((output) => output.points.every((point) => point.value === null))) {
1266
- warnings.push(`${spec.id}: not enough valid daily history to calculate ${spec.parameters.estimator ?? "close-to-close"} realized volatility.`);
1363
+ warnings.push(`${studyMessageName(spec)}: not enough valid daily history to calculate ${spec.parameters.estimator ?? "close-to-close"} realized volatility.`);
1267
1364
  }
1268
1365
  resolved.push(...outputs);
1269
1366
  return;
@@ -1307,11 +1404,11 @@ function resolveStudySpecs(
1307
1404
  && inputCurrency !== pairedCurrency;
1308
1405
  if (spec.kind === "ratio" && differentCurrencies) {
1309
1406
  warnings.push(
1310
- `${spec.id}: ${spec.kind} inputs use different currencies (${inputCurrency} and ${pairedCurrency}); raw values are not FX-converted.`,
1407
+ `${studyMessageName(spec)}: ${spec.kind} inputs use different currencies (${inputCurrency} and ${pairedCurrency}); raw values are not FX-converted.`,
1311
1408
  );
1312
1409
  }
1313
1410
  if (spec.kind === "correlation" && input.nativeFrequency !== pairedInput.nativeFrequency) {
1314
- warnings.push(`${spec.id}: correlation mixes ${input.nativeFrequency} and ${pairedInput.nativeFrequency} observations; only matching observation times contribute.`);
1411
+ warnings.push(`${studyMessageName(spec)}: correlation mixes ${input.nativeFrequency} and ${pairedInput.nativeFrequency} observations; only matching observation times contribute.`);
1315
1412
  }
1316
1413
  outputs = resolvePairStudy(spec, input, pairedInput, color);
1317
1414
  }
@@ -1319,7 +1416,7 @@ function resolveStudySpecs(
1319
1416
  spec.kind !== "volume"
1320
1417
  && (outputs.length === 0 || outputs.every((output) => output.points.length === 0))
1321
1418
  ) {
1322
- warnings.push(`${spec.id}: not enough valid history to calculate ${spec.kind}.`);
1419
+ warnings.push(`${studyMessageName(spec)}: not enough valid history to calculate ${spec.kind}.`);
1323
1420
  }
1324
1421
  resolved.push(...outputs);
1325
1422
  });
@@ -0,0 +1,121 @@
1
+ import { logFailure } from "../utils/log-failure";
2
+ import { fetchHistoryResult } from "../sources/history-result";
3
+ import type { PriceHistoryResult } from "../types/price-history";
4
+ import { CHART_RESOLUTION_STEP_MS } from "./resolution";
5
+ import type { DataProvider } from "../types/data-provider";
6
+ import type { TickerFinancials } from "../types/financials";
7
+ import { getPricePointTimestamp } from "../utils/price-history";
8
+ import { type ChartResolveSources, type ChartResolveCache, requestContext } from "./resolve-context";
9
+ import { DAY_MS } from "./chart-bounds";
10
+ import { priceHistoryAcquisitionIdentity, priceHistoryTailAcquisition, priceHistoryWindowsAgree, mergePriceHistoryWindows } from "./history-acquisition";
11
+
12
+ /** Recent-window requests for the same history, shared by every chart asking at once. */
13
+ const tailRequestsInFlight = new WeakMap<DataProvider, Map<string, Promise<PriceHistoryResult | null>>>();
14
+
15
+ /** Older tails are left to a full history refresh rather than a recent-window request. */
16
+ const MAX_TAIL_RECONCILE_LAG_MS = 3 * DAY_MS;
17
+
18
+ function latestObservationTime(points: TickerFinancials["priceHistory"]): number {
19
+ for (let index = points.length - 1; index >= 0; index -= 1) {
20
+ const point = points[index]!;
21
+ const time = getPricePointTimestamp(point);
22
+ if (Number.isFinite(time) && Number.isFinite(point.close)) return time;
23
+ }
24
+ return Number.NaN;
25
+ }
26
+
27
+ function samePricePoints(left: TickerFinancials["priceHistory"], right: TickerFinancials["priceHistory"]): boolean {
28
+ if (left.length !== right.length) return false;
29
+ for (let index = left.length - 1; index >= 0; index -= 1) {
30
+ const a = left[index]!;
31
+ const b = right[index]!;
32
+ if (a === b) continue;
33
+ if (getPricePointTimestamp(a) !== getPricePointTimestamp(b) || a.open !== b.open || a.high !== b.high
34
+ || a.low !== b.low || a.close !== b.close || a.volume !== b.volume) return false;
35
+ }
36
+ return true;
37
+ }
38
+
39
+ /**
40
+ * Refreshes the newest bars of each intraday history the last resolve plotted
41
+ * up to the present, so bars formed from quotes settle to the provider's
42
+ * version shortly after they close. Only the recent window is requested, and a
43
+ * window from a different source or session contract is never mixed in.
44
+ * Resolves true when any plotted bar changed.
45
+ */
46
+ export function reconcileChartTail(
47
+ sources: Pick<ChartResolveSources, "dataProvider">,
48
+ cache: ChartResolveCache,
49
+ now = Date.now(),
50
+ ): Promise<boolean> {
51
+ if (cache.tailReconcile) return cache.tailReconcile;
52
+ const provider = sources.dataProvider;
53
+ if (!provider || cache.liveTails.size === 0
54
+ || !(provider.getDetailedPriceHistoryWithMetadata || provider.getDetailedPriceHistory)) {
55
+ return Promise.resolve(false);
56
+ }
57
+ const run: Promise<boolean> = Promise.all([...cache.liveTails].map(async ([key, tail]) => {
58
+ const pending = cache.priceHistoryByRequest.get(key);
59
+ const loaded = pending ? await pending.catch(() => {
60
+ // Intentional ignore: resolveChartSpecPass already includes this history failure in the series errors.
61
+ return null;
62
+ }) : null;
63
+ if (!pending || !loaded || loaded.resolution !== tail.resolution) return false;
64
+ const accumulated = cache.accumulatedPriceHistory.get(tail.accumulationKey);
65
+ const latest = latestObservationTime(accumulated?.points ?? loaded.points);
66
+ if (!Number.isFinite(latest) || now - latest > MAX_TAIL_RECONCILE_LAG_MS) return false;
67
+ // The newest loaded bar may still have been forming when it was fetched.
68
+ // Whole-bar starts and whole-minute ends let every chart of this history
69
+ // that asks at the same boundary share one request and one cached window.
70
+ const step = CHART_RESOLUTION_STEP_MS[tail.resolution];
71
+ const start = Math.floor((latest - step) / step) * step;
72
+ const end = Math.ceil(now / 60_000) * 60_000;
73
+ const { symbol, exchange } = tail.source.instrument;
74
+ const context = { ...requestContext(tail.source), ...(tail.historySession ? { historySession: tail.historySession } : {}) };
75
+ const requestKey = JSON.stringify([symbol, exchange ?? "", context.brokerId ?? null,
76
+ context.brokerInstanceId ?? null, context.instrument ?? null, tail.resolution, start, end, tail.historySession ?? null]);
77
+ const shared = tailRequestsInFlight.get(provider) ?? new Map<string, Promise<PriceHistoryResult | null>>();
78
+ tailRequestsInFlight.set(provider, shared);
79
+ let request = shared.get(requestKey);
80
+ if (!request) {
81
+ const fetched = fetchHistoryResult(provider, symbol, exchange ?? "",
82
+ { kind: "detail", start: new Date(start), end: new Date(end), interval: tail.resolution }, context)
83
+ .catch(logFailure("time-series-resolve", "Could not reconcile chart history", null))
84
+ .finally(() => {
85
+ if (shared.get(requestKey) === fetched) shared.delete(requestKey);
86
+ });
87
+ shared.set(requestKey, fetched);
88
+ request = fetched;
89
+ }
90
+ const result = await request;
91
+ if (!result || result.resolution !== loaded.resolution || !!result.extendedHours !== !!loaded.extendedHours
92
+ || priceHistoryAcquisitionIdentity(result) !== priceHistoryAcquisitionIdentity(loaded)) return false;
93
+ const points = result.points.filter((point) => {
94
+ const time = getPricePointTimestamp(point);
95
+ return Number.isFinite(time) && time >= start && Number.isFinite(point.close);
96
+ });
97
+ // A full refetch that landed meanwhile supersedes this window.
98
+ if (points.length === 0 || cache.priceHistoryByRequest.get(key) !== pending) return false;
99
+ // A tail on another split basis (a split since the window was loaded) is not drawn beside it:
100
+ // the window stays as loaded, on one basis, until it is refetched whole.
101
+ if (!priceHistoryWindowsAgree(loaded.points, points, tail.resolution)) return false;
102
+ const refreshed = mergePriceHistoryWindows(loaded.points, points, tail.resolution);
103
+ if (samePricePoints(refreshed, loaded.points)) return false;
104
+ const acquisition = priceHistoryTailAcquisition(loaded, result);
105
+ // The merged bars are as current as the newer acquisition says.
106
+ cache.priceHistoryByRequest.set(key, Promise.resolve({ ...loaded, points: refreshed,
107
+ session: acquisition.session, sourceKey: acquisition.sourceKey, savedCopyBehind: acquisition.savedCopyBehind }));
108
+ if (accumulated) {
109
+ const accumulatedAcquisition = priceHistoryTailAcquisition(accumulated, result);
110
+ cache.accumulatedPriceHistory.set(tail.accumulationKey, { ...accumulated,
111
+ points: mergePriceHistoryWindows(accumulated.points, points, tail.resolution),
112
+ session: accumulatedAcquisition.session, sourceKey: accumulatedAcquisition.sourceKey });
113
+ }
114
+ cache.revision += 1;
115
+ return true;
116
+ })).then((changes) => changes.some(Boolean)).finally(() => {
117
+ if (cache.tailReconcile === run) cache.tailReconcile = null;
118
+ });
119
+ cache.tailReconcile = run;
120
+ return run;
121
+ }