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
@@ -7,6 +7,7 @@ import { barSizeFromGap, medianBarGap } from "../../../time-series/bar-spacing";
7
7
  import { apiClient } from "../../../api-client";
8
8
  import { isPerpetualExchange } from "../../../tickers/perpetual-listing";
9
9
  import { CLOUD_NEWS_DELAY_HOURS, hasProAccess } from "../../../api-client/plan-rules";
10
+ import { savedCopyMark } from "../../../data/saved-copies";
10
11
 
11
12
  /**
12
13
  * Freshness declarations shared by built-in reports. Each `fn` report ends
@@ -97,6 +98,8 @@ export function quoteFreshnessFields(
97
98
  // A print older than its venue's latest session says nothing of where that market stands now (no "markets open" beside it).
98
99
  const session = !isQuoteBeforeLatestSession(quote, now);
99
100
  return {
101
+ // A quote served from its saved copy, its refresh having failed, marks the row drawn from it.
102
+ ...savedCopyMark(quote),
100
103
  ...(quote.dataSource === "live" || quote.dataSource === "delayed" ? { dataSource: quote.dataSource } : {}),
101
104
  stale: isQuoteStaleForCurrentSession(quote, now),
102
105
  ...(delayed ? { delayMinutes: delayedFeedLagMs(exchange) / 60_000 } : {}),
@@ -13,6 +13,20 @@ export interface ResearchTradeProjection {
13
13
  contract?: BrokerContractRef;
14
14
  mode?: "simulation" | "live" | "unknown";
15
15
  busy?: boolean;
16
+ /**
17
+ * Moves one of `levels` to a price: a draft's price changes in the ticket, and a working order's change
18
+ * opens there for review. Nothing reaches the broker before the ticket's review and confirm.
19
+ */
20
+ moveLevel?(id: string, value: number): void;
21
+ /** The ticket prices a price picked on the chart can set now; the first is the one picked without a choice. */
22
+ priceFields?: readonly ResearchTradePriceField[];
23
+ /** Sets a ticket price to one picked on the chart. */
24
+ setPrice?(field: string, value: number): void;
25
+ }
26
+ interface ResearchTradePriceField {
27
+ field: string;
28
+ /** As the pick's hint says it: "limit", "stop", "take profit", "stop loss". */
29
+ label: string;
16
30
  }
17
31
  export interface ResearchTrading extends ResearchTradeProjection {
18
32
  symbol: string;
@@ -25,6 +39,15 @@ export interface ResearchTrading extends ResearchTradeProjection {
25
39
  /** Opens the ticket; without a side it keeps its draft, and Buy or Sell is chosen in it. */
26
40
  open(side?: "BUY" | "SELL"): void;
27
41
  close(): void;
42
+ /** A press on the chart takes the keyboard back from the ticket, even a field typing in it. */
43
+ focusChart?(): void;
44
+ /** The ticket price the chart beside it is picking, while it is. */
45
+ picking?: ResearchTradePriceField;
46
+ /** Picks a ticket price on the chart beside it, the first of `priceFields` without a field. */
47
+ pickPrice?(field?: string): void;
48
+ /** Hands the picked price to the ticket, which takes the keyboard back. */
49
+ finishPick?(value: number): void;
50
+ cancelPick?(): void;
28
51
  /** This ticker's working orders and latest fills, for Overview; absent without a trading profile. */
29
52
  orders?(width: number): ReactNode;
30
53
  publish(projection: ResearchTradeProjection): void;
@@ -4,6 +4,8 @@ import type { PluginPersistence } from "../../../types/plugin";
4
4
  export interface DatedObservation {
5
5
  date: string;
6
6
  value: number | null;
7
+ /** The day the value was released, when the source says (FRED's newest monthly figure). */
8
+ releasedOn?: string;
7
9
  }
8
10
 
9
11
  export interface SeriesCacheMetadata {
@@ -141,8 +141,9 @@ export function SeriesListDetail<Row, Column extends DataTableColumn, R extends
141
141
  isNavigable,
142
142
  renderSectionHeader,
143
143
  emptyStateTitle,
144
+ status,
144
145
  renderDetail,
145
- }: Pick<DataTableViewProps<Row, Column>, "renderCell" | "isNavigable" | "renderSectionHeader" | "emptyStateTitle"> & {
146
+ }: Pick<DataTableViewProps<Row, Column>, "renderCell" | "isNavigable" | "renderSectionHeader" | "emptyStateTitle" | "status"> & {
146
147
  list: ReturnType<typeof useSeriesList<unknown, R>>;
147
148
  width: number;
148
149
  height: number;
@@ -206,6 +207,7 @@ export function SeriesListDetail<Row, Column extends DataTableColumn, R extends
206
207
  renderCell={renderCell}
207
208
  renderSectionHeader={renderSectionHeader}
208
209
  emptyStateTitle={emptyStateTitle}
210
+ status={status}
209
211
  />
210
212
  </Box>
211
213
  </Box>
@@ -74,7 +74,8 @@ function normalizeInstanceIdPart(value: string): string {
74
74
  return encodeURIComponent(value.trim().toUpperCase()).replace(/%/g, "~");
75
75
  }
76
76
 
77
- function buildTickerSurfaceInstanceId(paneId: string, symbol: string, viewKey?: string): string {
77
+ /** `short-interest:AAPL`, `ticker-research:GGAL:ADR`: the one window a function keeps for a listing. */
78
+ export function buildTickerSurfaceInstanceId(paneId: string, symbol: string, viewKey?: string): string {
78
79
  const parts = [paneId, normalizeInstanceIdPart(symbol)];
79
80
  if (viewKey) parts.push(normalizeInstanceIdPart(viewKey));
80
81
  return parts.join(":");
@@ -192,10 +192,11 @@ export function expectedMove(
192
192
  sigmaPercent: sigma != null && Number.isFinite(sigma) ? sigma / spot * 100 : null, strike: atm?.strike ?? null };
193
193
  }
194
194
 
195
- /** 25-delta and 90/110 skew in decimal annualized IV; OVDV reads them on each expiry's fitted smile. */
195
+ /** 25-delta and 90/110 skew in decimal annualized IV; OVDV reads them on each expiry's fitted smile, from its forward. */
196
196
  export interface SkewMetrics {
197
197
  put25: number | null;
198
198
  call25: number | null;
199
+ /** IV at the forward (ATMF): the butterfly and the term slope are measured from it. */
199
200
  atm: number | null;
200
201
  putCallSkew: number | null;
201
202
  riskReversal: number | null;
@@ -21,6 +21,8 @@ function normalizeCloudRecords(payload: CloudShortInterestPayload): ShortInteres
21
21
  // FINRA publishes no float, so percent of float stays unknown rather than
22
22
  // being invented from a float measured on a different date.
23
23
  shortPercentFloat: point.shortPercentFloat ?? null,
24
+ ...(typeof point.sharesOutstanding === "number" && point.sharesOutstanding > 0
25
+ ? { sharesOutstanding: point.sharesOutstanding, sharesOutstandingAsOf: point.sharesOutstandingAsOf ?? null } : {}),
24
26
  });
25
27
  }
26
28
  return records;
@@ -4,6 +4,7 @@ import type {
4
4
  HeadlessPaneDefinition,
5
5
  HeadlessPaneLoadArgs,
6
6
  } from "../../../types/plugin";
7
+ import { DATE_OPTION_EXAMPLES, isoDateOption } from "../../../utils/calendar-date";
7
8
  import { formatCompact, formatNumber } from "../../../utils/format";
8
9
  import type { ShortInterestResult } from "./client";
9
10
  import {
@@ -11,13 +12,15 @@ import {
11
12
  sortRows,
12
13
  type SortPreference,
13
14
  } from "./model";
14
- import { shortInterestFreshness } from "./schedule";
15
+ import { shortInterestFreshness, shortInterestPublished } from "./schedule";
15
16
  import type { ShortInterestRecord } from "./types";
16
17
  import { loadShortInterestWithFloat } from "./watch-client";
17
18
  import { REPORT_ROWS, REPORT_SORT } from "../shared/headless-options";
18
19
 
19
20
  const SHORT_INTEREST_COLUMNS: HeadlessPaneColumn[] = [
20
21
  { key: "settlementDate", header: "Date" },
22
+ // When FINRA published the settlement: what a trader could see that day.
23
+ { key: "publishedDate", header: "Published" },
21
24
  {
22
25
  key: "sharesShort",
23
26
  header: "Shares Short",
@@ -42,6 +45,8 @@ const SHORT_INTEREST_COLUMNS: HeadlessPaneColumn[] = [
42
45
  align: "right",
43
46
  format: (value) => value == null ? "-" : `${formatNumber(Number(value), 2)}%`,
44
47
  },
48
+ // The day of the share count the float is held within.
49
+ { key: "floatAsOf", header: "Float As Of", align: "right" },
45
50
  ];
46
51
 
47
52
  export interface ShortInterestHeadlessDependencies {
@@ -53,7 +58,11 @@ export interface ShortInterestHeadlessDependencies {
53
58
  }
54
59
 
55
60
  const defaultDependencies: ShortInterestHeadlessDependencies = {
56
- loadRecords: (symbol, _args, ctx) => loadShortInterestWithFloat(symbol, { marketData: ctx.marketData, client: ctx.apiClient }),
61
+ loadRecords: (symbol, args, ctx) => loadShortInterestWithFloat(symbol, {
62
+ marketData: ctx.marketData,
63
+ client: ctx.apiClient,
64
+ pointInTime: typeof args.options.asOf === "string" && args.options.asOf !== "",
65
+ }),
57
66
  };
58
67
 
59
68
  export function createShortInterestHeadless(
@@ -71,7 +80,7 @@ export function createShortInterestHeadless(
71
80
  discovery: {
72
81
  limitations: [
73
82
  "Borrow fee, utilization and locate availability are not available: FINRA short interest counts shares short, not what they cost to borrow.",
74
- "Percent of float is the latest settlement's shares short over the current float; earlier settlements stay blank.",
83
+ "Percent of float is each settlement's shares short over the float of its day: the 10-K or 10-Q share count known when FINRA published it, at the part of today's count that floats; the latest is over the current float. A settlement no count was known for stays blank.",
75
84
  ],
76
85
  },
77
86
  usListingsOnly: "FINRA short interest",
@@ -84,6 +93,13 @@ export function createShortInterestHeadless(
84
93
  values: [{ value: "newest" }, { value: "oldest" }],
85
94
  defaultValue: "newest",
86
95
  },
96
+ {
97
+ key: "asOf",
98
+ type: "string",
99
+ placeholder: "date",
100
+ description: `Only the settlements FINRA had published by this date, as ${DATE_OPTION_EXAMPLES}, each on the share count known then.`,
101
+ normalize: (value) => isoDateOption(value, "as-of"),
102
+ },
87
103
  {
88
104
  key: "limit",
89
105
  shot: REPORT_ROWS,
@@ -109,26 +125,39 @@ export function createShortInterestHeadless(
109
125
  direction: args.options.order === "oldest" ? "asc" : "desc",
110
126
  };
111
127
  const limit = Number(args.options.limit);
112
- const rows = sortRows(buildRows(records), preference)
128
+ const asOf = typeof args.options.asOf === "string" && args.options.asOf ? args.options.asOf : null;
129
+ // A settlement published after the date was not known on it.
130
+ const published = asOf ? buildRows(records).filter((row) => row.publishedDate !== "-" && row.publishedDate <= asOf) : buildRows(records);
131
+ const rows = sortRows(published, preference)
113
132
  .slice(0, limit)
114
133
  .map((row) => ({
115
134
  settlementDate: row.settlementDate,
135
+ publishedDate: row.publishedDate === "-" ? null : row.publishedDate,
116
136
  sharesShort: row.record.sharesShort,
117
137
  daysToCover: row.record.shortRatio,
118
138
  averageDailyVolume: row.record.averageDailyVolume,
119
139
  shortPercentFloat: row.record.shortPercentFloat,
140
+ floatAsOf: row.floatAsOf === "-" ? null : row.floatAsOf,
120
141
  }));
121
- const freshness = shortInterestFreshness(rows.map((row) => row.settlementDate).sort().at(-1));
142
+ const schedule = shortInterestFreshness(rows.map((row) => row.settlementDate).sort().at(-1));
143
+ // A cut at a past date is as current as that date allowed, not stale by today's clock.
144
+ const freshness = schedule && asOf ? { ...schedule, maxAgeMinutes: Number.POSITIVE_INFINITY } : schedule;
145
+ // A date before the history held: say where it starts rather than show nothing.
146
+ const first = records.map((record) => record.settlementDate.toISOString().slice(0, 10)).sort()[0];
147
+ const errors = [
148
+ ...(asOf && first && rows.length === 0
149
+ ? [`No ${symbol} settlement held was published by ${asOf}: the history starts ${first}, published ${shortInterestPublished(first) ?? "later"}.`]
150
+ : []),
151
+ ...(latestSettlements ? [cloudSessionRequired
152
+ ? "Full history needs a Gloom Cloud sign-in; showing the latest two settlements."
153
+ : "Full history unavailable; showing the latest two settlements."] : []),
154
+ ];
122
155
  return {
123
156
  rows,
124
157
  ...(freshness ? { freshness } : {}),
125
- ...(latestSettlements ? {
126
- complete: false,
127
- errors: [cloudSessionRequired
128
- ? "Full history needs a Gloom Cloud sign-in; showing the latest two settlements."
129
- : "Full history unavailable; showing the latest two settlements."],
130
- } : {}),
131
- metadata: { symbol, order: args.options.order, ...(source ? { source } : {}) },
158
+ ...(latestSettlements ? { complete: false } : {}),
159
+ ...(errors.length ? { errors } : {}),
160
+ metadata: { symbol, order: args.options.order, ...(asOf ? { asOf } : {}), ...(source ? { source } : {}) },
132
161
  };
133
162
  },
134
163
  };
@@ -13,6 +13,8 @@ import { SHORT_WATCH_SCOPE_OPTIONS, ShortWatchPane } from "./watch-pane";
13
13
  import { withCommandTab } from "../shared/command-tab";
14
14
  import { followsWithoutFinraOverride, shortVolumeSettings } from "../short-volume";
15
15
  import { shortVolumeHeadless } from "../short-volume/headless";
16
+ import { publicationCache } from "./publications-client";
17
+ import { publicationHeadless } from "./publications-headless";
16
18
  import { isKnownNonUsListing } from "../../../utils/sec";
17
19
 
18
20
 
@@ -21,6 +23,7 @@ let disposeConnection: (() => void) | null = null;
21
23
  export const shortInterestModule: PluginModule = {
22
24
  series: SHORT_INTEREST_SERIES,
23
25
  setup(ctx) {
26
+ publicationCache.attach(ctx.persistence);
24
27
  attachShortInterestHealth(ctx.connectionHealth);
25
28
  disposeConnection = ctx.connectionHealth.registerSource({
26
29
  id: SHORT_INTEREST_CONNECTION_ID,
@@ -42,6 +45,7 @@ export const shortInterestModule: PluginModule = {
42
45
  },
43
46
 
44
47
  dispose() {
48
+ publicationCache.reset();
45
49
  disposeConnection?.();
46
50
  disposeConnection = null;
47
51
  resetShortInterestHealth();
@@ -84,7 +88,7 @@ export const shortInterestModule: PluginModule = {
84
88
  paneId: "short-interest",
85
89
  label: "Short Interest",
86
90
  // The current snapshot only carries the current and prior settlement dates.
87
- description: "Bi-monthly short interest settlements from FINRA with days to cover and average daily volume. Daily volume is the other tab.",
91
+ description: "Bi-monthly short interest settlements from FINRA with days to cover and average daily volume. Daily volume, fails to deliver and weekly ATS/OTC volume share the pane.",
88
92
  keywords: ["short", "interest", "si", "shorts", "days", "cover"],
89
93
  shortcut: "SI",
90
94
  }), "interest"),
@@ -103,6 +107,24 @@ export const shortInterestModule: PluginModule = {
103
107
  }), "volume"),
104
108
  headless: shortVolumeHeadless,
105
109
  },
110
+ {
111
+ ...withCommandTab(createTickerSurfacePaneTemplate({
112
+ id: "fails-to-deliver-pane", paneId: "short-interest", label: "Fails to Deliver",
113
+ description: "The short interest pane on Fails to deliver: outstanding fail shares by settlement date and CUSIP from SEC publications.",
114
+ keywords: ["fails to deliver", "fails-to-deliver", "ftd", "settlement", "failures", "sec"],
115
+ shortcut: "FTD", publicShare: true,
116
+ }), "ftd"),
117
+ headless: publicationHeadless("ftd"),
118
+ },
119
+ {
120
+ ...withCommandTab(createTickerSurfacePaneTemplate({
121
+ id: "otc-volume-pane", paneId: "short-interest", label: "Weekly ATS / OTC Volume",
122
+ description: "The short interest pane on Weekly ATS/OTC: FINRA weekly ATS and non-ATS shares and trades, including dark-pool volume.",
123
+ keywords: ["dark pool", "dark pools", "ats", "non-ats", "otc", "off-exchange", "weekly", "volume", "finra"],
124
+ shortcut: "ATS", publicShare: true,
125
+ }), "ats"),
126
+ headless: publicationHeadless("ats"),
127
+ },
106
128
  {
107
129
  id: "short-watch-pane",
108
130
  paneId: "short-watch",
@@ -3,10 +3,13 @@ import { spanDigits } from "../../../components/chart-table";
3
3
  import type { CompositeAxisDomain } from "../../../components/chart/composite/types";
4
4
  import { formatCompact, formatNumber } from "../../../utils/format";
5
5
  import { compareSortValues } from "../../../utils/sort-values";
6
+ import { shortInterestPublished } from "./schedule";
6
7
  import type { ShortInterestRecord } from "./types";
7
8
 
8
9
  export type ShortInterestColumnId =
9
10
  | "settlementDate"
11
+ | "publishedDate"
12
+ | "floatAsOf"
10
13
  | "sharesShort"
11
14
  | "shortRatio"
12
15
  | "averageDailyVolume"
@@ -18,6 +21,10 @@ export interface ShortInterestRow {
18
21
  key: string;
19
22
  record: ShortInterestRecord;
20
23
  settlementDate: string;
24
+ /** When FINRA published the settlement, `YYYY-MM-DD`; `-` when the date is not one. */
25
+ publishedDate: string;
26
+ /** The day of the share count the percent of float is read on; `-` when it is undated or there is none. */
27
+ floatAsOf: string;
21
28
  sharesShort: string;
22
29
  shortRatio: string;
23
30
  averageDailyVolume: string;
@@ -91,24 +98,50 @@ export function settlementPercentFloat(
91
98
  return record.shortPercentFloat ?? percentOfFloat(record.sharesShort, floatShares);
92
99
  }
93
100
 
101
+ /** The float a percent of float is read against today, and the share count it floats out of. */
102
+ export interface CurrentFloat {
103
+ floatShares: number | null | undefined;
104
+ /** YYYY-MM-DD the float was counted, when it is dated (the statistics' `floatSharesAsOf`); a source's own float is not. */
105
+ floatAsOf?: string | null;
106
+ /** The share count the float is held within (the statistics' shares outstanding). */
107
+ sharesOutstanding?: number | null;
108
+ statistics?: SettlementFloatStatistics | null;
109
+ }
110
+
94
111
  /**
95
- * The newest settlement's percent of float where the source gives none, read
96
- * against the current float as SIW reads it (settlementPercentFloat). The
97
- * float is undated, so the settlements before it stay blank rather than take
98
- * today's float.
112
+ * Each settlement's percent of float where the source gives none. The newest
113
+ * is read against the current float, as SIW and the statistics read it
114
+ * (settlementPercentFloat). An earlier one is read against the float of its
115
+ * own day: the 10-K or 10-Q share count known when FINRA published it
116
+ * (`sharesOutstanding` on the record) times the part of today's count that
117
+ * floats. A settlement without such a count stays blank rather than take
118
+ * today's float. `pointInTime` reads the newest the same way, so a report
119
+ * cut at a date (`--as-of`) uses no count filed after its settlements.
99
120
  */
100
- export function withLatestPercentFloat(
121
+ export function withPercentFloat(
101
122
  records: readonly ShortInterestRecord[],
102
- floatShares: number | null | undefined,
103
- statistics?: SettlementFloatStatistics | null,
123
+ current: CurrentFloat,
124
+ { pointInTime = false }: { pointInTime?: boolean } = {},
104
125
  ): ShortInterestRecord[] {
105
- const latest = records.reduce<ShortInterestRecord | null>(
126
+ const latest = pointInTime ? null : records.reduce<ShortInterestRecord | null>(
106
127
  (best, record) => (!best || record.settlementDate.getTime() > best.settlementDate.getTime() ? record : best),
107
128
  null,
108
129
  );
109
- return records.map((record) => (record === latest
110
- ? { ...record, shortPercentFloat: settlementPercentFloat(record, floatShares, statistics) }
111
- : record));
130
+ const { floatShares, sharesOutstanding } = current;
131
+ const floating = finiteNumber(floatShares) && finiteNumber(sharesOutstanding) && floatShares > 0 && sharesOutstanding > 0
132
+ ? Math.min(1, floatShares / sharesOutstanding) : null;
133
+ return records.map((record) => {
134
+ if (record === latest) {
135
+ const percent = settlementPercentFloat(record, floatShares, current.statistics);
136
+ return { ...record, shortPercentFloat: percent, floatAsOf: percent != null ? current.floatAsOf ?? null : null };
137
+ }
138
+ if (record.shortPercentFloat != null || floating == null || !finiteNumber(record.sharesOutstanding)) return record;
139
+ return {
140
+ ...record,
141
+ shortPercentFloat: percentOfFloat(record.sharesShort, record.sharesOutstanding * floating),
142
+ floatAsOf: record.sharesOutstandingAsOf ?? null,
143
+ };
144
+ });
112
145
  }
113
146
 
114
147
  export function buildRows(records: ShortInterestRecord[]): ShortInterestRow[] {
@@ -116,6 +149,8 @@ export function buildRows(records: ShortInterestRecord[]): ShortInterestRow[] {
116
149
  key: `${record.settlementDate.toISOString()}:${index}`,
117
150
  record,
118
151
  settlementDate: formatDate(record.settlementDate),
152
+ publishedDate: shortInterestPublished(formatDate(record.settlementDate)) ?? "-",
153
+ floatAsOf: record.shortPercentFloat != null && record.floatAsOf ? record.floatAsOf : "-",
119
154
  sharesShort: formatMaybeCompact(record.sharesShort),
120
155
  shortRatio: formatRatio(record.shortRatio),
121
156
  averageDailyVolume: formatMaybeCompact(record.averageDailyVolume),
@@ -127,6 +162,10 @@ function sortValue(row: ShortInterestRow, columnId: ShortInterestColumnId): stri
127
162
  switch (columnId) {
128
163
  case "settlementDate":
129
164
  return row.record.settlementDate.getTime();
165
+ case "publishedDate":
166
+ return row.publishedDate === "-" ? null : row.publishedDate;
167
+ case "floatAsOf":
168
+ return row.floatAsOf === "-" ? null : row.floatAsOf;
130
169
  case "sharesShort":
131
170
  return row.record.sharesShort;
132
171
  case "shortRatio":
@@ -146,28 +185,35 @@ export function sortRows(rows: ShortInterestRow[], preference: SortPreference):
146
185
 
147
186
  /**
148
187
  * The columns sit together rather than spread across the pane, so the figures
149
- * of one settlement read as a row, and fit a 60-column pane without a
150
- * horizontal scrollbar. FINRA settlement history carries no float, so percent of float
151
- * is null for every row unless the latest one is read against the current float
152
- * (`withLatestPercentFloat`). Keeping the column with nothing in it drew a header over a
153
- * column of dashes, which reads as missing data rather than data the source never had.
188
+ * of one settlement read as a row: when it settled, when FINRA published it
189
+ * (what a trader could see that day), then its figures. FINRA settlement
190
+ * history carries no float, so percent of float is null for every row unless
191
+ * it is read against a float (`withPercentFloat`), dated where that float is:
192
+ * an earlier settlement's by the count it is read on. Keeping the column with nothing in it drew a
193
+ * header over a column of dashes, which reads as missing data rather than
194
+ * data the source never had.
154
195
  */
155
196
  export function buildColumns(
156
197
  records: readonly ShortInterestRecord[] = [],
157
198
  ): ShortInterestColumn[] {
158
199
  const hasPercentFloat = records.some((record) => record.shortPercentFloat != null);
159
- const dateWidth = 12;
200
+ const hasFloatDate = records.some((record) => record.shortPercentFloat != null && !!record.floatAsOf);
201
+ const dateWidth = 11;
160
202
  const sharesWidth = 12;
161
203
  const ratioWidth = 12;
162
- const percentWidth = 10;
204
+ const percentWidth = 9;
163
205
  return [
164
206
  { id: "settlementDate", label: "DATE", width: dateWidth, align: "left" },
207
+ { id: "publishedDate", label: "PUBLISHED", width: dateWidth, align: "left" },
165
208
  { id: "sharesShort", label: "SHARES SHORT", width: sharesWidth, align: "right" },
166
209
  { id: "shortRatio", label: "DAYS TO COVER", width: ratioWidth, align: "right" },
167
210
  { id: "averageDailyVolume", label: "AVG VOL", width: 11, align: "right" },
168
211
  ...(hasPercentFloat
169
212
  ? [{ id: "shortPercentFloat" as const, label: "% FLOAT", width: percentWidth, align: "right" as const }]
170
213
  : []),
214
+ ...(hasFloatDate
215
+ ? [{ id: "floatAsOf" as const, label: "FLOAT AS OF", width: dateWidth + 1, align: "right" as const }]
216
+ : []),
171
217
  ];
172
218
  }
173
219
 
@@ -202,7 +248,12 @@ export function shortInterestFigures(records: readonly ShortInterestRecord[]): S
202
248
  return [
203
249
  { id: "shares", label: "Shares short", value: formatMaybeCompact(latest.sharesShort), detail: formatDate(latest.settlementDate) },
204
250
  ...(latest.shortRatio != null ? [{ id: "days", label: "Days to cover", value: formatRatio(latest.shortRatio) }] : []),
205
- ...(latest.shortPercentFloat != null ? [{ id: "float", label: "% float", value: formatPercent(latest.shortPercentFloat) }] : []),
251
+ ...(latest.shortPercentFloat != null ? [{
252
+ id: "float",
253
+ label: "% float",
254
+ value: formatPercent(latest.shortPercentFloat),
255
+ ...(latest.floatAsOf ? { detail: `as of ${latest.floatAsOf}` } : {}),
256
+ }] : []),
206
257
  ...(change != null && prior ? [{
207
258
  id: "change",
208
259
  label: "Change",
@@ -12,7 +12,9 @@ import {
12
12
  usePaneFooter,
13
13
  type DataTableCell,
14
14
  } from "../../../components";
15
+ import { createDataTableCsv } from "../../../components/data-table/export";
15
16
  import { loadingErrorFooterInfo, usePaneRefreshKey } from "../../../components/data-table/table-pane";
17
+ import { dropColumnsToFit } from "../../../components/ui/table-layout";
16
18
  import { useAsyncResource } from "../../../react/async-resource";
17
19
  import { colors } from "../../../theme/colors";
18
20
  import { Box, TextAttributes } from "../../../ui";
@@ -32,11 +34,17 @@ import {
32
34
  type ShortInterestRow,
33
35
  type SortPreference,
34
36
  } from "./model";
37
+ import { shortInterestPublicationSegments } from "./schedule";
35
38
  import type { ShortInterestRecord } from "./types";
36
39
  import { loadShortInterestWithFloat } from "./watch-client";
37
40
  import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
38
41
 
39
42
  const EMPTY_RECORDS: ShortInterestRecord[] = [];
43
+ /**
44
+ * A narrow pane gives up the float's date before it scrolls: the figures above
45
+ * the chart date the newest settlement's float, and exports keep the column.
46
+ */
47
+ const DROP_ORDER = ["floatAsOf"] as const;
40
48
  const rowKey = (row: ShortInterestRow) => row.key;
41
49
  const rowDate = (row: ShortInterestRow) => row.record.settlementDate;
42
50
 
@@ -62,7 +70,8 @@ function ShortInterestView({ width, height, focused }: { width: number; height:
62
70
 
63
71
  const rows = useMemo(() => buildRows(records), [records]);
64
72
  const sortedRows = useMemo(() => sortRows(rows, sortPreference), [rows, sortPreference]);
65
- const columns = useMemo(() => buildColumns(records), [records]);
73
+ const allColumns = useMemo(() => buildColumns(records), [records]);
74
+ const columns = useMemo(() => dropColumnsToFit(allColumns, width - 1, DROP_ORDER), [allColumns, width]);
66
75
  // The header row, plus the scrollbar row once the columns overflow the pane.
67
76
  const tableChromeRows = chartTableChromeRows(columns, width);
68
77
  const effectiveKey = sortedRows.some((row) => row.key === selectedKey) ? selectedKey : sortedRows[0]?.key ?? null;
@@ -91,6 +100,8 @@ function ShortInterestView({ width, height, focused }: { width: number; height:
91
100
  switch (column.id) {
92
101
  case "settlementDate":
93
102
  return { text: row.settlementDate, color: colors.textDim };
103
+ case "publishedDate":
104
+ return { text: row.publishedDate, color: colors.textDim };
94
105
  case "sharesShort":
95
106
  return { text: row.sharesShort, color: colors.textBright, attributes: TextAttributes.BOLD };
96
107
  case "shortRatio":
@@ -99,12 +110,17 @@ function ShortInterestView({ width, height, focused }: { width: number; height:
99
110
  return { text: row.averageDailyVolume, color: colors.textDim };
100
111
  case "shortPercentFloat":
101
112
  return { text: row.shortPercentFloat, color: colors.text };
113
+ case "floatAsOf":
114
+ return { text: row.floatAsOf, color: colors.textDim };
102
115
  }
103
116
  }, []);
104
117
 
118
+ // When the newest settlement shown was published and the next is due, as `fn SI` dates it.
119
+ const latestSettlement = useMemo(() => rows.map((row) => row.settlementDate).sort().at(-1) ?? null, [rows]);
105
120
  usePaneFooter("short-interest", () => ({
106
121
  info: [
107
122
  ...loadingErrorFooterInfo(status === "loading", status === "error" ? error?.slice(0, 60) : null),
123
+ ...shortInterestPublicationSegments(latestSettlement),
108
124
  ...(latestSettlements ? [{
109
125
  id: "source",
110
126
  parts: [{
@@ -113,7 +129,7 @@ function ShortInterestView({ width, height, focused }: { width: number; height:
113
129
  }],
114
130
  }] : []),
115
131
  ],
116
- }), [cloudSessionRequired, error, status, latestSettlements]);
132
+ }), [cloudSessionRequired, error, status, latestSettlements, latestSettlement]);
117
133
 
118
134
  if (!ticker || !symbol) {
119
135
  return <EmptyState title="No ticker selected." message="Select a ticker to view short interest." />;
@@ -150,6 +166,7 @@ function ShortInterestView({ width, height, focused }: { width: number; height:
150
166
  onHeaderClick={handleHeaderClick}
151
167
  getItemKey={rowKey}
152
168
  renderCell={renderCell}
169
+ exportCsv={() => createDataTableCsv({ columns: allColumns, items: sortedRows, renderCell })}
153
170
  selectedTextOverridesCellColor
154
171
  emptyStateTitle="No data"
155
172
  />
@@ -0,0 +1,82 @@
1
+ import { apiClient } from "../../../api-client";
2
+ import type { FailsToDeliverPayload, OtcVolumePayload, ShortSidePublication } from "../../../api-client/short-side-publications";
3
+ import { createPluginCache } from "../../../data/plugin-cache";
4
+ import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
5
+
6
+ export type PublicationKind = "ftd" | "ats";
7
+ export type PublicationPayload = FailsToDeliverPayload | OtcVolumePayload;
8
+ export const publicationCache = createPluginCache<PublicationPayload>({
9
+ kind: "short-side-publications", source: "gloom-cloud", schemaVersion: 2,
10
+ policy: { staleMs: 60 * 60_000, expireMs: 30 * 24 * 60 * 60_000 },
11
+ });
12
+ const date = (value: unknown): value is string => typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)
13
+ && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
14
+ const dateOrNull = (value: unknown) => value === null || date(value);
15
+ const count = (value: unknown): value is number => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
16
+ const countOrNull = (value: unknown) => value === null || count(value);
17
+ const sharesOrNull = (value: unknown) => value === null || typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= Number.MAX_SAFE_INTEGER;
18
+ const numberOrNull = (value: unknown) => value === null || typeof value === "number" && Number.isFinite(value) && value >= 0;
19
+ const url = (value: unknown): value is string => typeof value === "string" && /^https?:\/\//.test(value);
20
+
21
+ function validateEnvelope(payload: ShortSidePublication<unknown>, symbol: string): boolean {
22
+ return !!payload && payload.symbol === symbol && ["available", "partial", "unavailable"].includes(payload.status)
23
+ && dateOrNull(payload.asOf) && dateOrNull(payload.sourceAsOf)
24
+ && (payload.loadedAt === null || typeof payload.loadedAt === "string" && Number.isFinite(Date.parse(payload.loadedAt)))
25
+ && typeof payload.fetchedAt === "string" && Number.isFinite(Date.parse(payload.fetchedAt))
26
+ && !!payload.source && typeof payload.source.name === "string" && url(payload.source.url) && typeof payload.source.cadence === "string"
27
+ && Array.isArray(payload.warnings) && payload.warnings.every((warning) => typeof warning === "string")
28
+ && Array.isArray(payload.history) && !!payload.coverage && date(payload.coverage.from) && date(payload.coverage.to)
29
+ && payload.coverage.from <= payload.coverage.to && count(payload.coverage.publications)
30
+ && (payload.history.length > 0 ? payload.status !== "unavailable" && payload.asOf !== null : payload.status === "unavailable" && payload.asOf === null);
31
+ }
32
+
33
+ /** Reject mixed symbols, duplicate identities and inconsistent category totals; preserve missing values. */
34
+ export function validatePublication(payload: PublicationPayload, symbol: string, kind: PublicationKind): PublicationPayload {
35
+ const invalid = () => { throw new Error(`The server returned invalid ${kind === "ftd" ? "fails-to-deliver" : "weekly off-exchange volume"} history`); };
36
+ if (!validateEnvelope(payload, symbol)) return invalid();
37
+ let previous = "";
38
+ const identities = new Set<string>();
39
+ for (const row of payload.history) {
40
+ if (!row || !url(row.sourceUrl)) return invalid();
41
+ let observed: string;
42
+ let identity: string;
43
+ if (kind === "ftd") {
44
+ if (!("date" in row) || !date(row.date) || (typeof row.cusip !== "string" || !row.cusip.trim()) || !count(row.quantity)
45
+ || !numberOrNull(row.price) || typeof row.description !== "string") return invalid();
46
+ observed = row.date;
47
+ identity = `${row.date}:${row.cusip}`;
48
+ } else {
49
+ if (!("weekStart" in row) || !date(row.weekStart) || new Date(row.weekStart).getUTCDay() !== 1
50
+ || ![row.atsShares, row.nonAtsShares, row.totalShares].every(sharesOrNull)
51
+ || ![row.atsTrades, row.nonAtsTrades].every(countOrNull)
52
+ || !numberOrNull(row.atsSharePercent) || row.atsSharePercent !== null && row.atsSharePercent > 100) return invalid();
53
+ const complete = row.atsShares !== null && row.nonAtsShares !== null;
54
+ const total = complete ? row.atsShares! + row.nonAtsShares! : null;
55
+ if (row.totalShares !== total || (!total ? row.atsSharePercent !== null
56
+ : row.atsSharePercent === null || Math.abs(row.atsSharePercent - row.atsShares! / total * 100) > 0.0001)) return invalid();
57
+ observed = row.weekStart;
58
+ identity = observed;
59
+ }
60
+ if (observed < previous || observed < payload.coverage.from || observed > payload.coverage.to || identities.has(identity)) return invalid();
61
+ previous = observed;
62
+ identities.add(identity);
63
+ }
64
+ if (payload.history.length && payload.asOf !== previous) return invalid();
65
+ return payload;
66
+ }
67
+
68
+ export async function fetchPublication(symbol: string, kind: PublicationKind,
69
+ client: Pick<typeof apiClient, "getCloudFailsToDeliver" | "getCloudOtcVolume"> = apiClient): Promise<PublicationPayload> {
70
+ try {
71
+ const payload = kind === "ftd" ? await client.getCloudFailsToDeliver(symbol) : await client.getCloudOtcVolume(symbol);
72
+ return validatePublication(payload, symbol, kind);
73
+ } catch (error) {
74
+ throw unavailableOnServer(error, `${kind === "ftd" ? "Fails to deliver" : "Weekly off-exchange volume"} is not available yet.`, [404, 503]);
75
+ }
76
+ }
77
+ const cacheKey = (symbol: string, kind: PublicationKind) => `${kind}:${symbol}`;
78
+ export const cachedPublication = (symbol: string, kind: PublicationKind) =>
79
+ cachedCloudResource(publicationCache, cacheKey(symbol, kind), (data) => validatePublication(data, symbol, kind));
80
+ export const loadPublication = (symbol: string, kind: PublicationKind, force = false) =>
81
+ loadCloudResource(publicationCache, cacheKey(symbol, kind), () => fetchPublication(symbol, kind),
82
+ { force, validate: (data) => validatePublication(data, symbol, kind) });