gloomberb 0.16.3 → 0.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/signed-in/adapter.ts +33 -2
  30. package/src/brokers/signed-in/client.ts +10 -0
  31. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  32. package/src/brokers/signed-in/sign-in.ts +62 -5
  33. package/src/capabilities/registry.ts +8 -1
  34. package/src/capabilities/types.ts +2 -0
  35. package/src/cli/commands/crypto-hints.ts +12 -4
  36. package/src/cli/commands/market.ts +359 -99
  37. package/src/cli/commands/options-chain.ts +172 -0
  38. package/src/cli/commands/options-leaps.ts +422 -0
  39. package/src/cli/commands/overview.ts +164 -26
  40. package/src/cli/commands/search.ts +29 -8
  41. package/src/cli/commands/system.ts +99 -19
  42. package/src/cli/commands/themes.ts +64 -0
  43. package/src/cli/commands/ticker.ts +62 -30
  44. package/src/cli/desktop-pane-shot.ts +97 -16
  45. package/src/cli/errors.ts +25 -2
  46. package/src/cli/freshness.ts +7 -4
  47. package/src/cli/fx-pair.ts +57 -0
  48. package/src/cli/help.ts +14 -1
  49. package/src/cli/index-roots.ts +51 -0
  50. package/src/cli/index.ts +9 -6
  51. package/src/cli/listing-arg.ts +211 -3
  52. package/src/cli/not-a-ticker.ts +37 -0
  53. package/src/cli/options.ts +50 -6
  54. package/src/cli/pane-functions/capabilities.ts +8 -2
  55. package/src/cli/pane-functions/catalog.ts +13 -2
  56. package/src/cli/pane-functions/data.ts +36 -5
  57. package/src/cli/pane-functions/dom.ts +21 -13
  58. package/src/cli/pane-functions/freshness.ts +205 -26
  59. package/src/cli/pane-functions/function-help.ts +67 -22
  60. package/src/cli/pane-functions/headless.ts +5 -4
  61. package/src/cli/pane-functions/index.ts +3 -0
  62. package/src/cli/pane-functions/market-session.ts +133 -0
  63. package/src/cli/pane-functions/options.ts +15 -4
  64. package/src/cli/pane-functions/report-notices.ts +63 -0
  65. package/src/cli/pane-functions/report.ts +4 -0
  66. package/src/cli/pane-functions/resolver.ts +4 -6
  67. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  68. package/src/cli/pane-functions/screenshot.ts +105 -23
  69. package/src/cli/registry.ts +2 -1
  70. package/src/cli/result.ts +50 -13
  71. package/src/cli/row-window.ts +76 -0
  72. package/src/cli/scroll-truncation.ts +34 -0
  73. package/src/components/chart/composite/composite-chart.tsx +43 -4
  74. package/src/components/chart/composite/legend.tsx +39 -6
  75. package/src/components/chart/composite/rasterizer.ts +64 -4
  76. package/src/components/chart/composite/series-cues.ts +71 -0
  77. package/src/components/chart/composite/text-renderer.ts +16 -8
  78. package/src/components/chart/composite/types.ts +3 -0
  79. package/src/components/command-bar/commands/direct/index.ts +9 -1
  80. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  81. package/src/components/command-bar/commands/registry.ts +7 -0
  82. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  83. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  84. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  85. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  86. package/src/components/command-bar/routes/root/results.ts +40 -10
  87. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  88. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  89. package/src/components/command-bar/surface/index.tsx +18 -4
  90. package/src/components/command-bar/theme-picker.tsx +48 -13
  91. package/src/components/command-bar/workflow/ops.ts +5 -3
  92. package/src/components/data-table/view.tsx +28 -9
  93. package/src/components/layout/floating-pane.tsx +6 -2
  94. package/src/components/layout/market-summary.ts +23 -9
  95. package/src/components/layout/pane/content.tsx +46 -29
  96. package/src/components/layout/pane/cycle-order.ts +2 -11
  97. package/src/components/layout/pane/header.tsx +2 -1
  98. package/src/components/layout/pane/title.ts +1 -1
  99. package/src/components/layout/presentation.ts +22 -0
  100. package/src/components/layout/shell/active-drag.ts +1 -1
  101. package/src/components/layout/shell/drag/index.ts +4 -0
  102. package/src/components/layout/shell/drag/live.ts +76 -5
  103. package/src/components/layout/shell/drag/runtime.ts +8 -8
  104. package/src/components/layout/shell/index.tsx +47 -6
  105. package/src/components/layout/shell/layout-state.ts +5 -11
  106. package/src/components/layout/shell/menu.ts +28 -1
  107. package/src/components/layout/shell/pane/actions.ts +2 -1
  108. package/src/components/layout/shell/pane/layers.tsx +51 -10
  109. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  110. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  111. package/src/components/layout/shell/shortcuts.ts +5 -2
  112. package/src/components/layout/shell/visible-layout.ts +44 -1
  113. package/src/components/layout/status-bar.tsx +4 -1
  114. package/src/components/markdown-text.tsx +9 -5
  115. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  116. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  117. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  118. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  119. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  120. package/src/components/onboarding/pro-step-layout.ts +86 -0
  121. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  122. package/src/components/ui/button.tsx +10 -0
  123. package/src/components/ui/data-table/index.tsx +38 -0
  124. package/src/components/ui/data-table/types.ts +8 -1
  125. package/src/components/ui/data-table/visible-range.ts +2 -0
  126. package/src/components/ui/icon.tsx +11 -2
  127. package/src/components/ui/index.ts +1 -1
  128. package/src/core/connection-health.ts +1 -1
  129. package/src/core/state/app/layout-reducer.ts +89 -3
  130. package/src/core/state/app/state.ts +8 -5
  131. package/src/core/state/app/types.ts +13 -2
  132. package/src/data/config/layout.ts +55 -21
  133. package/src/data/config/store/fold-panes.ts +149 -0
  134. package/src/data/config/store/migrations.ts +7 -0
  135. package/src/data/config/store/node.ts +27 -6
  136. package/src/data/config/store/normalize.ts +37 -3
  137. package/src/i18n/es.ts +14 -1
  138. package/src/i18n/ja.ts +14 -1
  139. package/src/i18n/ko.ts +14 -1
  140. package/src/i18n/zh-cn.ts +14 -1
  141. package/src/i18n/zh-tw.ts +14 -1
  142. package/src/layout/desks.ts +1 -2
  143. package/src/layout/pane-layout-move.ts +264 -0
  144. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  145. package/src/layout/pane-manager/index.ts +1 -0
  146. package/src/market-data/coordinator/entries.ts +7 -1
  147. package/src/market-data/coordinator/quotes.ts +2 -2
  148. package/src/market-data/market/freshness.ts +118 -18
  149. package/src/market-data/market/status.ts +40 -0
  150. package/src/market-data/options-alternatives.ts +69 -0
  151. package/src/market-data/quotes/contributions.ts +19 -2
  152. package/src/market-data/quotes/metadata.ts +16 -0
  153. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  154. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  155. package/src/plugins/builtin/account-management/model.ts +1 -0
  156. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  157. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  158. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  159. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  160. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  161. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  162. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  163. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  164. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  165. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  166. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  167. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  168. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  169. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  170. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  171. package/src/plugins/builtin/chat/channels.ts +34 -36
  172. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  173. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  175. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  176. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  177. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  178. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  179. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  180. package/src/plugins/builtin/chat/conversations.ts +256 -0
  181. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  182. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  183. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  184. package/src/plugins/builtin/chat/index.tsx +40 -2
  185. package/src/plugins/builtin/chat/layout.ts +2 -0
  186. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  187. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  188. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  189. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  190. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  191. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  192. package/src/plugins/builtin/chat/pane.tsx +8 -0
  193. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  194. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  195. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  196. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  197. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  198. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  199. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  200. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  201. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  202. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  203. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  204. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  205. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  206. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  207. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  208. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  209. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  210. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  211. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  212. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  213. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  214. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  215. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  216. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  217. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  218. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  219. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  220. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  221. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  222. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  223. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  224. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  225. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  226. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  227. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  228. package/src/plugins/builtin/composite-plugins.ts +107 -64
  229. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  230. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  231. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  232. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  233. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  234. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  235. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  236. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  237. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  238. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  239. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  240. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  241. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  242. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  243. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  244. package/src/plugins/builtin/econ/index.tsx +68 -22
  245. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  246. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  247. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  248. package/src/plugins/builtin/executives/data.ts +17 -6
  249. package/src/plugins/builtin/executives/pane.tsx +16 -7
  250. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  251. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  252. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  253. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  254. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  255. package/src/plugins/builtin/gpu/headless.ts +9 -3
  256. package/src/plugins/builtin/gpu/model.ts +14 -5
  257. package/src/plugins/builtin/help/index.tsx +1 -0
  258. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  259. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  260. package/src/plugins/builtin/holders/headless.ts +6 -1
  261. package/src/plugins/builtin/holders/pane.tsx +3 -0
  262. package/src/plugins/builtin/insider/index.tsx +4 -4
  263. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  264. package/src/plugins/builtin/iv-history/client.ts +12 -2
  265. package/src/plugins/builtin/iv-history/format.ts +33 -2
  266. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  267. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  268. package/src/plugins/builtin/iv-history/model.ts +61 -13
  269. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  270. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  271. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  272. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  273. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  275. package/src/plugins/builtin/kelly-sizer/pane.tsx +109 -39
  276. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  277. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  278. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  279. package/src/plugins/builtin/macro-day/client.ts +24 -0
  280. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  281. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  282. package/src/plugins/builtin/macro-day/model.ts +11 -6
  283. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  284. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  285. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  286. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  287. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  288. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  289. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  290. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  291. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  292. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  293. package/src/plugins/builtin/market-movers/model.ts +71 -17
  294. package/src/plugins/builtin/market-movers/table.ts +50 -23
  295. package/src/plugins/builtin/news/headless.ts +9 -2
  296. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  297. package/src/plugins/builtin/openfda/client.ts +1 -1
  298. package/src/plugins/builtin/options/carry.ts +95 -0
  299. package/src/plugins/builtin/options/index.tsx +58 -5
  300. package/src/plugins/builtin/options/strike-window.ts +183 -0
  301. package/src/plugins/builtin/options/table.ts +55 -3
  302. package/src/plugins/builtin/options/types.ts +8 -1
  303. package/src/plugins/builtin/options/view.tsx +161 -12
  304. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  305. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  306. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  307. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  308. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  309. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  310. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  311. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  312. package/src/plugins/builtin/pe-band/model.ts +2 -2
  313. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  314. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  315. package/src/plugins/builtin/perps/model.ts +16 -5
  316. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  317. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  318. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  319. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  320. package/src/plugins/builtin/plugin-module.ts +87 -4
  321. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  322. package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
  323. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  324. package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
  325. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  326. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  327. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  328. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  329. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  330. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  331. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  332. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  333. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  334. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  335. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  336. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  337. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  338. package/src/plugins/builtin/sec/index.tsx +5 -5
  339. package/src/plugins/builtin/sectors/client.ts +10 -2
  340. package/src/plugins/builtin/sectors/headless.ts +35 -6
  341. package/src/plugins/builtin/sectors/index.tsx +18 -1
  342. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  343. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  344. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  345. package/src/plugins/builtin/shared/members-menu.tsx +4 -4
  346. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  347. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  348. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  349. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  350. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  351. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  352. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  353. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  354. package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
  355. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  356. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  357. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  358. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  359. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  360. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  361. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  362. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  363. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  364. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  365. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  366. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  367. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  368. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  369. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  370. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  371. package/src/plugins/builtin/world-indices/client.ts +22 -4
  372. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  373. package/src/plugins/builtin/world-indices/model.ts +4 -2
  374. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  375. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  376. package/src/plugins/builtin/world-venue-map/index.ts +1 -1
  377. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  378. package/src/plugins/builtin-editorial.ts +126 -0
  379. package/src/plugins/catalog-backend.ts +16 -9
  380. package/src/plugins/catalog-browser.ts +127 -63
  381. package/src/plugins/catalog-manifest.ts +73 -40
  382. package/src/plugins/catalog-ui.ts +26 -6
  383. package/src/plugins/extracted-plugins.ts +7 -1
  384. package/src/plugins/ownership.ts +150 -7
  385. package/src/plugins/plugin-functions.ts +54 -0
  386. package/src/plugins/plugin-off.ts +36 -0
  387. package/src/plugins/registry/host-actions.ts +16 -0
  388. package/src/plugins/registry/index.ts +84 -24
  389. package/src/plugins/registry/plugin-state.ts +25 -1
  390. package/src/plugins/runtime/context.tsx +29 -0
  391. package/src/react/input.ts +48 -1
  392. package/src/remote/app-host.tsx +5 -0
  393. package/src/remote/controller.ts +3 -53
  394. package/src/remote/matching.ts +69 -0
  395. package/src/remote/schema.ts +1 -1
  396. package/src/remote/semantic-tree.tsx +14 -0
  397. package/src/remote/types.ts +2 -0
  398. package/src/renderers/dom/desktop/icons.tsx +14 -0
  399. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  400. package/src/renderers/dom/host/box.tsx +1 -1
  401. package/src/renderers/dom/host/drag-shield.ts +17 -6
  402. package/src/renderers/dom/host/live-frame.ts +10 -2
  403. package/src/renderers/dom/input-host.tsx +6 -1
  404. package/src/renderers/dom/styles.css +13 -0
  405. package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
  406. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  407. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
  408. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  409. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  410. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  411. package/src/renderers/electrobun/view/main.tsx +4 -1
  412. package/src/renderers/opentui/input-host.tsx +6 -1
  413. package/src/renderers/opentui/start.tsx +9 -0
  414. package/src/shares/portable-layout.ts +16 -6
  415. package/src/sources/gloomberb-cloud/index.ts +19 -10
  416. package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
  417. package/src/sources/listing-symbols.ts +11 -5
  418. package/src/sources/provider-errors.ts +88 -3
  419. package/src/sources/provider-router/cached-routes.ts +8 -4
  420. package/src/sources/provider-router/financial-routes.ts +11 -5
  421. package/src/sources/provider-router/financials.ts +42 -15
  422. package/src/sources/provider-router/history.ts +27 -11
  423. package/src/sources/provider-router/index.ts +1 -1
  424. package/src/sources/provider-router/primary.ts +23 -5
  425. package/src/sources/sec-edgar.ts +68 -8
  426. package/src/sync/core-contributors.ts +4 -3
  427. package/src/test-support/cli-context.ts +2 -1
  428. package/src/test-support/data-provider.ts +34 -0
  429. package/src/test-support/shell-pane-keys.tsx +1 -0
  430. package/src/theme/series-colors.ts +46 -0
  431. package/src/theme/themes.ts +149 -0
  432. package/src/tickers/search/index.ts +22 -2
  433. package/src/tickers/search/ranking.ts +45 -1
  434. package/src/tickers/search/types.ts +3 -1
  435. package/src/time-series/resolve.ts +1 -14
  436. package/src/types/config.ts +61 -1
  437. package/src/types/headless.ts +11 -0
  438. package/src/types/instrument.ts +8 -0
  439. package/src/types/plugin.ts +49 -3
  440. package/src/ui/host.tsx +8 -4
  441. package/src/ui/index.tsx +2 -0
  442. package/src/utils/cli-output.ts +14 -1
  443. package/src/utils/exchanges.ts +13 -0
  444. package/src/utils/format.ts +2 -1
  445. package/src/utils/fx-market-hours.ts +34 -0
  446. package/src/utils/option-expiry.ts +126 -0
  447. package/src/utils/price-history.ts +39 -6
  448. package/src/utils/rpc-timeout-error.ts +6 -2
  449. package/src/utils/sec-eps-basis.ts +159 -30
  450. package/src/utils/sec.ts +38 -34
  451. package/src/utils/throttled-fetch.ts +51 -22
@@ -0,0 +1,149 @@
1
+ import type { HeadlessPaneDefinition, HeadlessPaneRow } from "../../../types/headless";
2
+ import { resolveHeadlessInstrument } from "../shared/headless-market-data";
3
+ import { MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
4
+ import { parseHedgeAmount } from "../options-scenario/hedge";
5
+ import { clipPriceHistoryToRange } from "../../../time-series/history-window";
6
+ import { formatNumber } from "../../../utils/format";
7
+ import { buildBinaryOutcomes, solveKellyFraction } from "./model";
8
+ import {
9
+ clampKellyHistoryYears,
10
+ estimateKellyHistoryInputs,
11
+ KELLY_HISTORY_DEFAULT_YEARS,
12
+ KELLY_HISTORY_MAX_YEARS,
13
+ KELLY_HISTORY_MIN_PERIODS,
14
+ kellyHistoryRange,
15
+ kellyHistoryRequest,
16
+ type KellyHistoryInputs,
17
+ } from "./history";
18
+ import { formatPct, formatSignedPct } from "./view";
19
+
20
+ /** The fractions of full Kelly a report sizes, largest first. */
21
+ const KELLY_FRACTIONS = [
22
+ { id: "full", label: "Full Kelly", fraction: 1 },
23
+ { id: "half", label: "Half Kelly", fraction: 0.5 },
24
+ { id: "quarter", label: "Quarter Kelly", fraction: 0.25 },
25
+ ] as const;
26
+
27
+ /** The inputs a ticker's monthly returns give, as the History tab shows them. */
28
+ function inputEntries(inputs: KellyHistoryInputs, edge: number) {
29
+ const payoff = inputs.downsideReturn < 0 ? inputs.upsideReturn / -inputs.downsideReturn : null;
30
+ return [
31
+ { key: "window", label: "Window", value: { start: inputs.start, end: inputs.end, months: inputs.periods },
32
+ formatted: `${inputs.start} to ${inputs.end}, ${inputs.periods} monthly returns` },
33
+ { key: "winRate", label: "Win rate", value: inputs.winProbability,
34
+ formatted: `${formatPct(inputs.winProbability)} (${inputs.wins} of ${inputs.periods} months up)` },
35
+ { key: "averageGain", label: "Average gain", value: inputs.upsideReturn, formatted: `${formatSignedPct(inputs.upsideReturn)} a month` },
36
+ { key: "averageLoss", label: "Average loss", value: inputs.downsideReturn, formatted: `${formatSignedPct(inputs.downsideReturn)} a month` },
37
+ { key: "payoffRatio", label: "Payoff ratio", value: payoff, formatted: payoff == null ? "no down month" : `${formatNumber(payoff, 2)}x` },
38
+ { key: "edge", label: "Edge", value: edge, formatted: `${formatSignedPct(edge)} a month` },
39
+ ];
40
+ }
41
+
42
+ export const kellyHeadless: HeadlessPaneDefinition<"bundle"> = {
43
+ shape: "bundle",
44
+ argument: { kind: "ticker", description: "Ticker to size" },
45
+ freshness: { source: "Your inputs and Gloom Cloud", status: "not-a-feed", basis: "monthly returns from daily closes", cadence: "daily", oldest: null },
46
+ description: "Kelly sizing for a ticker from its own monthly returns: win rate, payoff ratio and edge over a lookback, then full, half and quarter Kelly as a share of a bankroll.",
47
+ describe: (args) => `Kelly sizing | ${args.symbols[0] ?? ""}`,
48
+ discovery: {
49
+ dataRequirements: ["Gloom Cloud daily closes over the lookback, and the latest quote for share counts"],
50
+ limitations: ["Win rate, gain and loss are the ticker's own non-overlapping 21-session returns; past months are not a forecast."],
51
+ },
52
+ options: [
53
+ {
54
+ key: "bankroll",
55
+ type: "string",
56
+ settingKey: "bankroll",
57
+ placeholder: "amount",
58
+ example: "--bankroll 250000",
59
+ description: "Bankroll in your base currency, such as 250000, 250k or 1.5m; sizes each Kelly fraction in money.",
60
+ normalize: (value) => parseHedgeAmount(value, "bankroll"),
61
+ },
62
+ {
63
+ key: "years",
64
+ type: "integer",
65
+ settingKey: "historyYears",
66
+ minimum: 1,
67
+ maximum: KELLY_HISTORY_MAX_YEARS,
68
+ defaultValue: KELLY_HISTORY_DEFAULT_YEARS,
69
+ description: "Lookback in years for the monthly returns.",
70
+ },
71
+ ],
72
+ async load(args, ctx) {
73
+ const { symbol, exchange = "" } = await resolveHeadlessInstrument(ctx, args.symbols[0]!);
74
+ const years = clampKellyHistoryYears(args.options.years);
75
+ const range = kellyHistoryRange(years);
76
+ // The quote and the one history window the lookback needs load together, through the
77
+ // coordinator every chart reads daily closes from (the History tab's request, cache included).
78
+ const [entry, quote] = await Promise.all([
79
+ new MarketDataCoordinator(ctx.marketData).loadChart(kellyHistoryRequest({ symbol, exchange }, years), { forceRefresh: ctx.refresh }),
80
+ ctx.marketData.getQuote(symbol, exchange, ctx.refresh ? { cacheMode: "refresh" } : undefined).catch(() => null),
81
+ ]);
82
+ const history = clipPriceHistoryToRange(resolveEntryValue(entry) ?? [], range);
83
+ ctx.signal.throwIfAborted();
84
+ const inputs = estimateKellyHistoryInputs(history, years);
85
+ if (!inputs) {
86
+ return {
87
+ complete: false,
88
+ unavailableSymbols: [symbol],
89
+ errors: [`${symbol}: fewer than ${KELLY_HISTORY_MIN_PERIODS} monthly returns in the last ${years} year${years === 1 ? "" : "s"}.`],
90
+ sections: [],
91
+ };
92
+ }
93
+ const solve = solveKellyFraction(buildBinaryOutcomes(inputs.winProbability, inputs.upsideReturn, inputs.downsideReturn));
94
+ const bankrollValue = args.options.bankroll;
95
+ const bankroll = typeof bankrollValue === "number" && bankrollValue > 0 ? bankrollValue : null;
96
+ const baseCurrency = ctx.config.baseCurrency || "USD";
97
+ const price = quote && quote.price > 0 && Number.isFinite(quote.price) ? quote.price : null;
98
+ // Shares only when the bankroll and the price are in one currency.
99
+ const sharePrice = price != null && quote?.currency === baseCurrency ? price : null;
100
+ const rows: HeadlessPaneRow[] = KELLY_FRACTIONS.map(({ id, label, fraction }) => {
101
+ const share = solve.fraction * fraction;
102
+ const position = bankroll == null ? null : share * bankroll;
103
+ return {
104
+ id,
105
+ kelly: label,
106
+ fraction: share,
107
+ position,
108
+ shares: position != null && sharePrice != null ? Math.floor(position / sharePrice) : null,
109
+ };
110
+ });
111
+ const notes = [
112
+ ...(solve.warning ? [solve.warning] : []),
113
+ ...(solve.fraction === 0 ? ["No positive edge over this window: every Kelly fraction sizes nothing."] : []),
114
+ ...(bankroll == null ? [`Add --bankroll <amount> to size each fraction in ${baseCurrency}.`] : []),
115
+ ];
116
+ return {
117
+ complete: true,
118
+ freshness: { asOf: inputs.end },
119
+ notes,
120
+ sections: [
121
+ { title: "Inputs", entries: inputEntries(inputs, solve.expectedReturn) },
122
+ {
123
+ title: bankroll == null ? "Sizing" : `Sizing on ${formatNumber(bankroll, 0)} ${baseCurrency}`,
124
+ columns: [
125
+ { key: "kelly", header: "Kelly" },
126
+ { key: "fraction", header: "% of bankroll", align: "right", format: (value) => typeof value === "number" ? formatPct(value, 2) : "--" },
127
+ ...(bankroll == null ? [] : [
128
+ { key: "position", header: `Position ${baseCurrency}`, align: "right" as const,
129
+ format: (value: unknown) => typeof value === "number" ? formatNumber(value, 0) : "--" },
130
+ ...(sharePrice == null ? [] : [{ key: "shares", header: "Shares", align: "right" as const,
131
+ format: (value: unknown) => typeof value === "number" ? formatNumber(value, 0) : "--" }]),
132
+ ]),
133
+ ],
134
+ rows,
135
+ },
136
+ ],
137
+ metadata: {
138
+ symbol,
139
+ inputs,
140
+ expectedReturn: solve.expectedReturn,
141
+ expectedLogGrowth: solve.expectedLogGrowth,
142
+ bankroll,
143
+ currency: baseCurrency,
144
+ price: price == null ? null : { value: price, currency: quote?.currency ?? null, asOf: quote?.lastUpdated ?? null },
145
+ method: "Binary Kelly on non-overlapping 21-session returns: win rate, mean gain and mean loss.",
146
+ },
147
+ };
148
+ },
149
+ };
@@ -0,0 +1,80 @@
1
+ import type { PricePoint } from "../../../types/financials";
2
+ import type { TimeRange } from "../../../time-series/range";
3
+ import type { ChartRequest, InstrumentRef } from "../../../market-data/request-types";
4
+ import { getPricePointTimestamp } from "../../../utils/price-history";
5
+
6
+ /** Sessions in one holding period: a month of trading days. */
7
+ export const KELLY_HISTORY_PERIOD_SESSIONS = 21;
8
+ export const KELLY_HISTORY_DEFAULT_YEARS = 5;
9
+ export const KELLY_HISTORY_MAX_YEARS = 5;
10
+ /** Fewer monthly returns than this say too little about a win rate to size on. */
11
+ export const KELLY_HISTORY_MIN_PERIODS = 6;
12
+
13
+ const YEAR_MS = 365.25 * 24 * 60 * 60_000;
14
+
15
+ /** The binary inputs a ticker's own monthly returns imply over a lookback window. */
16
+ export interface KellyHistoryInputs {
17
+ winProbability: number;
18
+ /** Mean return of the up months, as a fraction. */
19
+ upsideReturn: number;
20
+ /** Mean return of the down months, as a negative fraction (0 when none fell). */
21
+ downsideReturn: number;
22
+ /** Up months over all months. */
23
+ wins: number;
24
+ periods: number;
25
+ /** Close the first return starts from, and the last bar, as YYYY-MM-DD. */
26
+ start: string;
27
+ end: string;
28
+ years: number;
29
+ }
30
+
31
+ export function clampKellyHistoryYears(value: unknown): number {
32
+ const years = Math.round(Number(value));
33
+ return Number.isFinite(years) ? Math.min(KELLY_HISTORY_MAX_YEARS, Math.max(1, years)) : KELLY_HISTORY_DEFAULT_YEARS;
34
+ }
35
+
36
+ /** The shortest history range that covers the lookback, so a one-year window never loads five. */
37
+ export function kellyHistoryRange(years: number): TimeRange {
38
+ return clampKellyHistoryYears(years) <= 1 ? "1Y" : "5Y";
39
+ }
40
+
41
+ /** The daily closes the History tab and the KELLY report both read. */
42
+ export function kellyHistoryRequest(instrument: InstrumentRef, years: number): ChartRequest {
43
+ return { instrument, bufferRange: kellyHistoryRange(years), granularity: "resolution", resolution: "1d" };
44
+ }
45
+
46
+ const dateKey = (time: number) => new Date(time).toISOString().slice(0, 10);
47
+
48
+ /**
49
+ * Win rate, mean gain and mean loss of non-overlapping 21-session returns,
50
+ * counted back from the last daily close so the newest month is complete.
51
+ * Null when the window holds fewer than `KELLY_HISTORY_MIN_PERIODS` of them.
52
+ */
53
+ export function estimateKellyHistoryInputs(points: readonly PricePoint[], yearsInput: number): KellyHistoryInputs | null {
54
+ const years = clampKellyHistoryYears(yearsInput);
55
+ const closes = points
56
+ .map((point) => ({ time: getPricePointTimestamp(point), close: point.close }))
57
+ .filter((point) => Number.isFinite(point.time) && Number.isFinite(point.close) && point.close > 0)
58
+ .sort((left, right) => left.time - right.time);
59
+ const last = closes.at(-1);
60
+ if (!last) return null;
61
+ const cutoff = last.time - years * YEAR_MS;
62
+ const window = closes.filter((point) => point.time >= cutoff);
63
+ const picks: typeof window = [];
64
+ for (let index = window.length - 1; index >= 0; index -= KELLY_HISTORY_PERIOD_SESSIONS) picks.unshift(window[index]!);
65
+ const returns = picks.slice(1).map((point, index) => point.close / picks[index]!.close - 1);
66
+ if (returns.length < KELLY_HISTORY_MIN_PERIODS) return null;
67
+ const gains = returns.filter((value) => value > 0);
68
+ const losses = returns.filter((value) => value < 0);
69
+ const mean = (values: number[]) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : 0;
70
+ return {
71
+ winProbability: gains.length / returns.length,
72
+ upsideReturn: mean(gains),
73
+ downsideReturn: mean(losses),
74
+ wins: gains.length,
75
+ periods: returns.length,
76
+ start: dateKey(picks[0]!.time),
77
+ end: dateKey(last.time),
78
+ years,
79
+ };
80
+ }
@@ -2,6 +2,7 @@ import type { PaneTemplateContext, PaneTemplateCreateOptions } from "../../../ty
2
2
  import type { PluginModule } from "../plugin-module";
3
3
  import { DEFAULT_FLOATING_SIZE, KELLY_PANE_ID } from "./constants";
4
4
  import { KellySizerPane } from "./pane";
5
+ import { kellyHeadless } from "./headless";
5
6
 
6
7
  function resolveTemplateSymbol(context: PaneTemplateContext, options?: PaneTemplateCreateOptions): string | null {
7
8
  return options?.symbol
@@ -40,6 +41,7 @@ export const positionSizerModule: PluginModule = {
40
41
  const symbol = resolveTemplateSymbol(context, options);
41
42
  return symbol ? { params: { symbol }, placement: "floating" } : null;
42
43
  },
44
+ headless: kellyHeadless,
43
45
  },
44
46
  ],
45
47
  };
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { Box, useUiCapabilities } from "../../../ui";
3
3
  import { useShortcut } from "../../../react/input";
4
+ import { useRemoteUiNode } from "../../../remote/semantic-tree";
4
5
  import { colors } from "../../../theme/colors";
5
6
  import {
6
7
  EmptyState,
@@ -17,7 +18,9 @@ import {
17
18
  type QueryBarFilter,
18
19
  } from "../../../components";
19
20
  import type { PaneProps } from "../../../types/plugin";
20
- import { useFxRatesMap } from "../../../market-data/hooks";
21
+ import { useChartQueries, useFxRatesMap, useResolvedEntryValue } from "../../../market-data/hooks";
22
+ import { buildChartKey } from "../../../market-data/selectors";
23
+ import { clipPriceHistoryToRange } from "../../../time-series/history-window";
21
24
  import { useLiveTickerFinancials, useLiveTickerFinancialsMap } from "../../../state/hooks/live-ticker-financials";
22
25
  import { buildPortfolioFinancialsMap } from "../../../market-data/portfolio-financials";
23
26
  import { convertCurrency, formatCurrency } from "../../../utils/format";
@@ -27,6 +30,7 @@ import {
27
30
  getFocusedTickerSymbol,
28
31
  useAppSelector,
29
32
  usePaneInstance,
33
+ usePaneSettingValue,
30
34
  usePaneStateValue,
31
35
  usePaneAppConfig,
32
36
  } from "../../../state/app/context";
@@ -51,12 +55,20 @@ import {
51
55
  } from "./model";
52
56
  import {
53
57
  DEFAULT_KELLY_DRAFTS,
54
- KELLY_MODES,
58
+ KELLY_PANE_MODES,
59
+ type KellyPaneMode,
55
60
  type KellySizerDraft,
56
61
  type KellySizerModeDrafts,
57
- type KellySizingMode,
58
62
  type PredictionMarketKellyAssumptions,
59
63
  } from "./types";
64
+ import {
65
+ clampKellyHistoryYears,
66
+ estimateKellyHistoryInputs,
67
+ KELLY_HISTORY_DEFAULT_YEARS,
68
+ KELLY_HISTORY_MIN_PERIODS,
69
+ kellyHistoryRange,
70
+ kellyHistoryRequest,
71
+ } from "./history";
60
72
  import { KELLY_PANE_ID } from "./constants";
61
73
  import {
62
74
  buildKellyCurveXAxisLabels,
@@ -70,6 +82,7 @@ import type { StaticChartXMarker } from "../../../components/chart/static";
70
82
  import {
71
83
  KellyCurveSection,
72
84
  KellySensitivitySection,
85
+ buildKellyHistoryItems,
73
86
  buildKellyResultItems,
74
87
  } from "./sections";
75
88
  import { getPortfolioPositionValue, resolveActivePortfolioId, resolveKellyBankroll } from "./portfolio";
@@ -95,7 +108,13 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
95
108
  const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
96
109
  const { nativePaneChrome } = useUiCapabilities();
97
110
 
98
- const [mode, setMode] = usePaneStateValue<KellySizingMode>("mode", "binary");
111
+ // A pane opened from a KELLY report (fn, shot) sizes from the ticker's own history, as the report does.
112
+ const [reportYears] = usePaneSettingValue<number | null>("historyYears", null);
113
+ const [reportBankroll] = usePaneSettingValue<number | null>("bankroll", null);
114
+ const [mode, setMode] = usePaneStateValue<KellyPaneMode>("mode", reportYears != null ? "history" : "binary");
115
+ const [historyYears, setHistoryYears] = usePaneStateValue<number>("historyYears", clampKellyHistoryYears(reportYears ?? KELLY_HISTORY_DEFAULT_YEARS));
116
+ // History reads inputs off the ticker's monthly returns and sizes them as a binary bet.
117
+ const sizingMode = mode === "history" ? "binary" : mode;
99
118
  // A narrow terminal bar has no room for the view switch beside the ticker,
100
119
  // price and account (plus the Side and Portfolio filters when shown), so
101
120
  // there `s` stays a footer hint.
@@ -105,7 +124,10 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
105
124
  const [drafts, setDrafts] = usePaneStateValue<KellySizerModeDrafts>("drafts", cloneKellyDrafts());
106
125
  const [showSensitivity, setShowSensitivity] = usePaneStateValue<boolean>("showSensitivity", false);
107
126
  const [selectedPortfolioId, setSelectedPortfolioId] = usePaneStateValue<string | null>("portfolioId", null);
108
- const [bankrollOverride, setBankrollOverride] = usePaneStateValue<number | null>("bankrollOverride", null);
127
+ const [bankrollOverride, setBankrollOverride] = usePaneStateValue<number | null>(
128
+ "bankrollOverride",
129
+ typeof reportBankroll === "number" && reportBankroll > 0 ? reportBankroll : null,
130
+ );
109
131
  const [currentValueOverride, setCurrentValueOverride] = usePaneStateValue<number | null>("currentValueOverride", null);
110
132
  const [selectedFieldIndex, setSelectedFieldIndex] = useState(0);
111
133
  const [activeInputId, setActiveInputId] = useState<string | null>(null);
@@ -256,26 +278,57 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
256
278
  const bankroll = bankrollOverride ?? sourceBankroll;
257
279
  const currentValue = currentValueOverride ?? sourceCurrentValue;
258
280
  const price = positionFinancials?.quote?.price ?? null;
259
- const rawActiveDraft = drafts[mode] ?? DEFAULT_KELLY_DRAFTS[mode];
260
- const { commonAssumptions, updateCommon } = useKellyCommonAssumptions(rawActiveDraft);
261
- const activeDraft = useMemo(
262
- () => applyKellyCommonAssumptions(rawActiveDraft, commonAssumptions),
263
- [commonAssumptions, rawActiveDraft],
281
+ // The same daily closes, through the same request, as the KELLY report reads.
282
+ const historyRequest = useMemo(
283
+ () => mode === "history" && ticker
284
+ ? kellyHistoryRequest({ symbol: ticker.metadata.ticker, exchange: ticker.metadata.exchange ?? "" }, historyYears)
285
+ : null,
286
+ [historyYears, mode, ticker?.metadata.exchange, ticker?.metadata.ticker],
287
+ );
288
+ const historyEntries = useChartQueries(historyRequest ? [historyRequest] : []);
289
+ const historyEntry = historyRequest ? historyEntries.get(buildChartKey(historyRequest)) ?? null : null;
290
+ const historyPoints = useResolvedEntryValue(historyEntry);
291
+ const historyInputs = useMemo(
292
+ () => historyPoints
293
+ ? estimateKellyHistoryInputs(clipPriceHistoryToRange(historyPoints, kellyHistoryRange(historyYears)), historyYears)
294
+ : null,
295
+ [historyPoints, historyYears],
264
296
  );
297
+ const rawActiveDraft = drafts[sizingMode] ?? DEFAULT_KELLY_DRAFTS[sizingMode];
298
+ const { commonAssumptions, updateCommon } = useKellyCommonAssumptions(rawActiveDraft);
299
+ const activeDraft = useMemo(() => {
300
+ const draft = applyKellyCommonAssumptions(rawActiveDraft, commonAssumptions);
301
+ if (mode !== "history") return draft;
302
+ // Until the history loads there is no edge to size.
303
+ return {
304
+ ...draft,
305
+ winProbability: historyInputs?.winProbability ?? 0,
306
+ upsideReturn: historyInputs?.upsideReturn ?? 0,
307
+ downsideReturn: historyInputs?.downsideReturn ?? 0,
308
+ };
309
+ }, [commonAssumptions, historyInputs, mode, rawActiveDraft]);
265
310
 
266
311
  const updateDraft = useCallback((patch: Partial<KellySizerDraft>) => {
267
312
  setDrafts((current) => ({
268
313
  ...cloneKellyDrafts(current),
269
- [mode]: {
270
- ...(current[mode] ?? DEFAULT_KELLY_DRAFTS[mode]),
314
+ [sizingMode]: {
315
+ ...(current[sizingMode] ?? DEFAULT_KELLY_DRAFTS[sizingMode]),
271
316
  ...patch,
272
317
  } as KellySizerDraft,
273
318
  }));
274
- }, [mode, setDrafts]);
319
+ }, [sizingMode, setDrafts]);
275
320
 
276
- const fields = useMemo(
277
- () => buildModeFields({ mode, draft: activeDraft, updateDraft }),
278
- [activeDraft, mode, updateDraft],
321
+ const fields = useMemo<GridField[]>(
322
+ () => mode === "history"
323
+ ? [{
324
+ id: "history:years",
325
+ label: "Years",
326
+ value: historyYears,
327
+ valueText: String(historyYears),
328
+ onValue: (value) => setHistoryYears(clampKellyHistoryYears(value)),
329
+ }]
330
+ : buildModeFields({ mode: sizingMode, draft: activeDraft, updateDraft }),
331
+ [activeDraft, historyYears, mode, setHistoryYears, sizingMode, updateDraft],
279
332
  );
280
333
  const commonFields = useMemo(
281
334
  () => buildCommonFields({ common: commonAssumptions, updateCommon }),
@@ -307,35 +360,35 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
307
360
  const selectedField = gridFields[Math.min(selectedFieldIndex, Math.max(0, gridFields.length - 1))] ?? null;
308
361
  const result = useMemo(
309
362
  () => calculateKellySizing({
310
- mode,
363
+ mode: sizingMode,
311
364
  draft: activeDraft,
312
365
  bankroll,
313
366
  currentValue,
314
367
  price,
315
368
  }),
316
- [activeDraft, bankroll, currentValue, mode, price],
369
+ [activeDraft, bankroll, currentValue, price, sizingMode],
317
370
  );
318
- const sensitivity = useMemo(() => buildSensitivityGrid(mode, activeDraft), [activeDraft, mode]);
371
+ const sensitivity = useMemo(() => buildSensitivityGrid(sizingMode, activeDraft), [activeDraft, sizingMode]);
319
372
  const curveMaxFraction = useMemo(
320
- () => getKellyCurveMaxFraction(mode, activeDraft, [
373
+ () => getKellyCurveMaxFraction(sizingMode, activeDraft, [
321
374
  result.currentFraction,
322
375
  result.clippedFraction,
323
376
  result.fullKellyFraction,
324
377
  ]),
325
- [activeDraft, mode, result.clippedFraction, result.currentFraction, result.fullKellyFraction],
378
+ [activeDraft, sizingMode, result.clippedFraction, result.currentFraction, result.fullKellyFraction],
326
379
  );
327
380
  const curvePoints = useMemo(
328
- () => buildKellyCurvePoints(mode, activeDraft, curveMaxFraction),
329
- [activeDraft, curveMaxFraction, mode],
381
+ () => buildKellyCurvePoints(sizingMode, activeDraft, curveMaxFraction),
382
+ [activeDraft, curveMaxFraction, sizingMode],
330
383
  );
331
384
  const curveXAxisLabels = useMemo(() => buildKellyCurveXAxisLabels(curveMaxFraction), [curveMaxFraction]);
332
385
  const currentGrowth = useMemo(
333
- () => calculateExpectedLogGrowthAtFraction(mode, activeDraft, result.currentFraction),
334
- [activeDraft, mode, result.currentFraction],
386
+ () => calculateExpectedLogGrowthAtFraction(sizingMode, activeDraft, result.currentFraction),
387
+ [activeDraft, sizingMode, result.currentFraction],
335
388
  );
336
389
  const targetGrowth = useMemo(
337
- () => calculateExpectedLogGrowthAtFraction(mode, activeDraft, result.clippedFraction),
338
- [activeDraft, mode, result.clippedFraction],
390
+ () => calculateExpectedLogGrowthAtFraction(sizingMode, activeDraft, result.clippedFraction),
391
+ [activeDraft, sizingMode, result.clippedFraction],
339
392
  );
340
393
  const curveMarkers = useMemo<StaticChartXMarker[]>(() => {
341
394
  if (!Number.isFinite(curveMaxFraction) || curveMaxFraction <= 0) return [];
@@ -406,8 +459,21 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
406
459
  }
407
460
  }, { enabled: focused });
408
461
 
462
+ // A report or shot of the History tab is as of the last daily close it read, not when it ran.
463
+ useRemoteUiNode(mode === "history" && historyInputs ? {
464
+ role: "report-freshness",
465
+ label: "History freshness",
466
+ getMetadata: () => ({ asOf: historyInputs.end }),
467
+ } : null);
468
+ // History mode says what it is waiting for, or why it has no inputs, before any sizing warning.
469
+ const historyStatus = mode !== "history" || historyInputs ? null
470
+ : historyEntry?.error ? { text: historyEntry.error.message, tone: "negative" as const }
471
+ : historyPoints ? { text: `under ${KELLY_HISTORY_MIN_PERIODS} monthly returns`, tone: "warning" as const }
472
+ : { text: "loading history", tone: "muted" as const };
409
473
  usePaneFooter(KELLY_PANE_ID, () => ({
410
- info: bankroll <= 0 && ticker
474
+ info: historyStatus
475
+ ? [{ id: "history", parts: [historyStatus] }]
476
+ : bankroll <= 0 && ticker
411
477
  // Every size reads 0% until there is a bankroll; say so where status lives.
412
478
  ? [{ id: "bankroll", parts: [{ text: "no bankroll", tone: "warning" as const }] }]
413
479
  : result.warnings.length > 0
@@ -421,15 +487,15 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
421
487
  { id: "search", key: "/", label: "search", onPress: focusTickerSearch },
422
488
  ...(viewInBar ? [] : [{ id: "sensitivity", key: "s", label: showSensitivity ? "ensitivity off" : "ensitivity", onPress: toggleSensitivity }]),
423
489
  ],
424
- }), [activeInputId, bankroll, editSelectedField, focusTickerSearch, result.clipReasons, result.warnings, selectedField?.id, selectedField?.label, showSensitivity, ticker, toggleSensitivity, viewInBar]);
490
+ }), [activeInputId, bankroll, editSelectedField, focusTickerSearch, historyStatus?.text, historyStatus?.tone, result.clipReasons, result.warnings, selectedField?.id, selectedField?.label, showSensitivity, ticker, toggleSensitivity, viewInBar]);
425
491
 
426
492
  const portfolioTabs = useMemo(
427
493
  () => config.portfolios.map((portfolio) => ({ label: portfolio.name, value: portfolio.id })),
428
494
  [config.portfolios],
429
495
  );
430
- const modeTabs = useMemo(() => KELLY_MODES.map((entry) => ({ label: entry.label, value: entry.id })), []);
496
+ const modeTabs = useMemo(() => KELLY_PANE_MODES.map((entry) => ({ label: entry.label, value: entry.id })), []);
431
497
  const selectMode = (nextMode: string) => {
432
- setMode(nextMode as KellySizingMode);
498
+ setMode(nextMode as KellyPaneMode);
433
499
  setSelectedFieldIndex(0);
434
500
  activateInput(null);
435
501
  };
@@ -442,14 +508,18 @@ export function KellySizerPane({ focused, width, height }: PaneProps) {
442
508
  });
443
509
  const gridColumns = fieldGridColumns(width);
444
510
  const gridRows = fieldGridRows(gridFields, gridColumns);
445
- const resultItems = buildKellyResultItems({
446
- result,
447
- baseCurrency: config.baseCurrency,
448
- currentGrowth,
449
- targetGrowth,
450
- width,
451
- columns: gridColumns,
452
- });
511
+ const resultItems = [
512
+ // History's inputs lead: the window and the monthly figures the sizes come from.
513
+ ...(mode === "history" && historyInputs ? buildKellyHistoryItems(historyInputs) : []),
514
+ ...buildKellyResultItems({
515
+ result,
516
+ baseCurrency: config.baseCurrency,
517
+ currentGrowth,
518
+ targetGrowth,
519
+ width,
520
+ columns: gridColumns,
521
+ }),
522
+ ];
453
523
  const resultRows = statGridRows(resultItems, width, gridColumns);
454
524
  // Query bar, the mode strip when it is not in the title bar, inputs, results.
455
525
  const bodyRows = Math.max(0, height - 1 - tabRows - gridRows - resultRows);
@@ -13,11 +13,25 @@ import { colors, priceColor } from "../../../theme/colors";
13
13
  import { Box } from "../../../ui";
14
14
  import { displayWidth, formatCurrency, formatNumber } from "../../../utils/format";
15
15
  import type { KellySizingResult, SensitivityGrid } from "./types";
16
+ import type { KellyHistoryInputs } from "./history";
16
17
  import { formatPct, formatSignedPct } from "./view";
17
18
 
18
19
  /** The longest result label ("Full growth"), which sets the band's label column. */
19
20
  const RESULT_LABEL_CHARS = 11;
20
21
 
22
+ /** The inputs History reads off the ticker's monthly returns, ahead of the sizes. */
23
+ export function buildKellyHistoryItems(inputs: KellyHistoryInputs): StatItem[] {
24
+ const payoff = inputs.downsideReturn < 0 ? inputs.upsideReturn / -inputs.downsideReturn : null;
25
+ return [
26
+ // The dates take a row of their own so a narrow pane never cuts them.
27
+ { id: "window", label: "Window", value: `${inputs.start} to ${inputs.end}`, detail: `${inputs.periods} monthly returns`, wide: true },
28
+ { id: "winRate", label: "Win rate", value: formatPct(inputs.winProbability, 1), detail: `${inputs.wins}/${inputs.periods}` },
29
+ { id: "avgGain", label: "Avg gain", value: formatSignedPct(inputs.upsideReturn), color: priceColor(inputs.upsideReturn) },
30
+ { id: "avgLoss", label: "Avg loss", value: formatSignedPct(inputs.downsideReturn), color: priceColor(inputs.downsideReturn) },
31
+ { id: "payoff", label: "Payoff", value: payoff == null ? "—" : `${formatNumber(payoff, 2)}x` },
32
+ ];
33
+ }
34
+
21
35
  /**
22
36
  * The sizing result as one band under the inputs, laid out in the FieldGrid's
23
37
  * columns: the Kelly fractions, then the move in money, then risk, then log
@@ -105,7 +105,7 @@ export interface SensitivityGrid {
105
105
  cells: SensitivityGridCell[][];
106
106
  }
107
107
 
108
- export const KELLY_MODES: Array<{ id: KellySizingMode; label: string }> = [
108
+ const KELLY_MODES: Array<{ id: KellySizingMode; label: string }> = [
109
109
  { id: "binary", label: "Binary" },
110
110
  { id: "scenario", label: "Scenario" },
111
111
  { id: "risk-budget", label: "Risk" },
@@ -113,6 +113,14 @@ export const KELLY_MODES: Array<{ id: KellySizingMode; label: string }> = [
113
113
  { id: "asymmetric", label: "Asym" },
114
114
  ];
115
115
 
116
+ /** The pane's tabs: the sizing modes, and History, which reads a binary bet off the ticker's monthly returns. */
117
+ export type KellyPaneMode = KellySizingMode | "history";
118
+
119
+ export const KELLY_PANE_MODES: Array<{ id: KellyPaneMode; label: string }> = [
120
+ ...KELLY_MODES,
121
+ { id: "history", label: "History" },
122
+ ];
123
+
116
124
  export const DEFAULT_KELLY_COMMON_ASSUMPTIONS: KellyCommonAssumptions = {
117
125
  kellyFraction: 0.25,
118
126
  maxNameFraction: 0.08,
@@ -27,24 +27,32 @@ import {
27
27
 
28
28
  let dispatchRef: ((action: AppAction) => void) | null = null;
29
29
  let getStateRef: (() => { layout: LayoutConfig; termWidth: number; termHeight: number; focusedPaneId: string | null }) | null = null;
30
+ let restoreHiddenRef: ((layout: LayoutConfig) => LayoutConfig) | null = null;
30
31
 
32
+ /**
33
+ * `getState().layout` is the layout on screen. `restoreHidden` puts the panes
34
+ * it leaves out back into an edit of it before the edit is saved.
35
+ */
31
36
  export function setLayoutManagerDispatch(
32
37
  dispatch: (action: AppAction) => void,
33
38
  getState: () => { layout: LayoutConfig; termWidth: number; termHeight: number; focusedPaneId: string | null },
39
+ restoreHidden: (layout: LayoutConfig) => LayoutConfig,
34
40
  ) {
35
41
  dispatchRef = dispatch;
36
42
  getStateRef = getState;
43
+ restoreHiddenRef = restoreHidden;
37
44
  }
38
45
 
39
46
  function clearLayoutManagerDispatch() {
40
47
  dispatchRef = null;
41
48
  getStateRef = null;
49
+ restoreHiddenRef = null;
42
50
  }
43
51
 
44
52
  function persistLayout(layout: LayoutConfig) {
45
53
  if (!dispatchRef) return;
46
54
  dispatchRef({ type: "PUSH_LAYOUT_HISTORY" });
47
- dispatchRef({ type: "UPDATE_LAYOUT", layout });
55
+ dispatchRef({ type: "UPDATE_LAYOUT", layout: restoreHiddenRef ? restoreHiddenRef(layout) : layout });
48
56
  }
49
57
 
50
58
  function getFocusedPane(layout: LayoutConfig, focusedPaneId: string | null) {
@@ -1,3 +1,5 @@
1
+ import { apiClient, type CloudMacroReleaseDaysPayload } from "../../../api-client";
2
+ import { createPluginCache } from "../../../data/plugin-cache";
1
3
  import { getSharedMarketDataCoordinator, MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
2
4
  import type { InstrumentRef } from "../../../market-data/request-types";
3
5
  import { tickerHasListingSuffix } from "../../../sources/listing-symbols";
@@ -6,6 +8,28 @@ import type { PricePoint } from "../../../types/financials";
6
8
  import { abortable, abortError } from "../../../utils/async-deadline";
7
9
  import { errorMessage } from "../../../utils/errors";
8
10
  import { resolveExchangeTimeZone } from "../../../utils/exchanges";
11
+ import { bundledMacroReleases, mergeMacroReleases, type MacroReleaseList } from "./releases";
12
+
13
+ export const macroReleaseCache = createPluginCache<CloudMacroReleaseDaysPayload>({
14
+ kind: "macro-release-days", source: "gloom-cloud", schemaVersion: 1,
15
+ // A release lands at 8:30 or 14:00 ET; the last list read is kept for a terminal that goes offline.
16
+ policy: { staleMs: 30 * 60_000, expireMs: 90 * 86_400_000 },
17
+ });
18
+
19
+ /**
20
+ * The bundled list, extended by the server's when it reaches further. A
21
+ * failed read keeps the last list read; with none, the bundled list alone.
22
+ */
23
+ export async function loadMacroReleases(options: { force?: boolean } = {},
24
+ read: () => Promise<CloudMacroReleaseDaysPayload> = () => apiClient.getCloudMacroReleaseDays()): Promise<MacroReleaseList> {
25
+ const floor = bundledMacroReleases();
26
+ try {
27
+ const result = await macroReleaseCache.load("release-days", read, { force: options.force });
28
+ return mergeMacroReleases(floor, result.data);
29
+ } catch {
30
+ return floor;
31
+ }
32
+ }
9
33
 
10
34
  export interface MacroDayHistory {
11
35
  history: PricePoint[];