gloomberb 0.18.3 → 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 (886) 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/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -46,17 +46,20 @@ import { useResearchCloudSession } from "../shared/research-cloud-session";
46
46
  import { loadPortfolioRiskMarket } from "./risk-client";
47
47
  import { brokerPerformanceEvidence, parsePortfolioRiskEvidence } from "./risk-evidence";
48
48
  import { useBrokerPortfolioPerformance } from "./broker-performance";
49
+ import { parseAssetShifts, parseFxShifts, riskCashLines } from "./risk-book";
50
+ import { resolvePortfolioAccountState } from "../portfolio-list/summary";
49
51
  import {
50
52
  BASKET_VIEWS,
51
53
  buildPortfolioRisk,
54
+ HOLDINGS_SHARE_UNIT,
52
55
  HOLDINGS_SUMMARY_ROW_IDS,
56
+ isShockRow,
53
57
  portfolioRiskRequests,
54
58
  portfolioRiskTickers,
55
- riskCoverageHeadline,
59
+ riskCoverageLine,
56
60
  riskCoverageNotices,
57
61
  riskCoverageShortfall,
58
62
  riskPercentile,
59
- riskAmountBase,
60
63
  riskEvidence,
61
64
  riskReturnsCurrency,
62
65
  riskValue,
@@ -98,6 +101,10 @@ const LABEL_HEADERS: Record<RiskView, string> = {
98
101
  greeks: "Metric",
99
102
  };
100
103
  const EVIDENCE_VIEWS: ReadonlySet<RiskView> = new Set(["performance", "attribution", "greeks"]);
104
+ /** The risk view's tail rows, whose method the footer names. */
105
+ const TAIL_ROW_IDS: ReadonlySet<string> = new Set(["var", "es"]);
106
+ /** The views that read the basket's daily closes, and say to when; Holdings reads current marks. */
107
+ const DAILY_CLOSE_VIEWS: ReadonlySet<RiskView> = new Set(["risk", "factors", "correlation", "stress"]);
101
108
  const ATTRIBUTION_PARTS = [
102
109
  { id: "allocation", label: "Allocation" },
103
110
  { id: "selection", label: "Selection" },
@@ -113,13 +120,17 @@ const bareValue = (row: RiskDisplayRow) => row.value == null ? "--" : row.value.
113
120
  const HOLDINGS_SUMMARY_LABELS: Record<string, string> = { top: "Largest", "top-five": "Top five" };
114
121
  /** The book concentration figures that lead the holdings view, as a summary band. */
115
122
  function holdingsSummaryItems(rows: RiskDisplayRow[]): StatItem[] {
116
- return rows.map((row) => ({
117
- id: row.id,
118
- label: HOLDINGS_SUMMARY_LABELS[row.id] ?? row.label,
119
- value: row.value == null ? "--" : row.unit === "% value" ? `${row.value.toFixed(2)}%` : row.value.toFixed(2),
120
- // The same note sits on both shares.
121
- detail: row.unit === "% value" ? "of market value" : row.detail,
122
- }));
123
+ return rows.map((row) => {
124
+ // A snapshot saved before the shares named their base reads "% value".
125
+ const share = row.unit === HOLDINGS_SHARE_UNIT || row.unit === "% value";
126
+ return {
127
+ id: row.id,
128
+ label: HOLDINGS_SUMMARY_LABELS[row.id] ?? row.label,
129
+ value: row.value == null ? "--" : share ? `${row.value.toFixed(2)}%` : row.value.toFixed(2),
130
+ // The same note sits on both shares: what they divide.
131
+ detail: share ? (row.unit === HOLDINGS_SHARE_UNIT ? "of holdings, cash aside" : "of market value") : row.detail,
132
+ };
133
+ });
123
134
  }
124
135
  /**
125
136
  * Cumulative return of the basket and of its benchmark across the window of
@@ -224,7 +235,8 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
224
235
  const instance = usePaneInstance(),
225
236
  renderer = useRendererHost();
226
237
  const portfolios = useAppSelector((state) => state.config.portfolios),
227
- tickers = useAppSelector((state) => state.tickers);
238
+ tickers = useAppSelector((state) => state.tickers),
239
+ brokerAccounts = useAppSelector((state) => state.brokerAccounts);
228
240
  const focusedCollection = useAppSelector((state) =>
229
241
  getFocusedCollectionId(state),
230
242
  );
@@ -271,8 +283,33 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
271
283
  const [equity] = usePaneSettingValue("equityShift", -10),
272
284
  [rates] = usePaneSettingValue("rateShift", 100),
273
285
  [volatility] = usePaneSettingValue("volShift", 10),
286
+ [fxShift] = usePaneSettingValue("fxShift", ""),
287
+ [assetShift] = usePaneSettingValue("assetShift", ""),
274
288
  [since] = usePaneSettingValue("riskSince", "trailing"),
275
289
  [weights] = usePaneSettingValue("riskWeights", "13f");
290
+ // Currency and price shocks as the settings give them; one that does not read is named in the footer, not applied.
291
+ const shocks = useMemo(() => {
292
+ const errors: string[] = [];
293
+ function read<T>(text: unknown, parse: (text: string, flag: string) => T[], label: string): T[] {
294
+ if (!String(text ?? "").trim()) return [];
295
+ try {
296
+ return parse(String(text), label);
297
+ } catch (error) {
298
+ errors.push(error instanceof Error ? error.message : `${label} unreadable`);
299
+ return [];
300
+ }
301
+ }
302
+ return { fx: read(fxShift, parseFxShifts, "FX shift"), assets: read(assetShift, parseAssetShifts, "Asset shift"), errors };
303
+ }, [fxShift, assetShift]);
304
+ // The cash beside the holdings: the broker account's last saved balances by currency, else the portfolio's own.
305
+ const cash = useMemo(() => {
306
+ if (!portfolio) return [];
307
+ const account = resolvePortfolioAccountState(portfolio, { config, brokerAccounts }, { status: null, accounts: [] })?.account;
308
+ return riskCashLines(portfolio, account);
309
+ }, [brokerAccounts, config, portfolio]);
310
+ // The currencies the load prices, kept by value so a resynced account with the same ones reloads nothing.
311
+ const cashKey = cash.map((line) => line.currency).join(",");
312
+ const cashCurrencies = useMemo(() => (cashKey ? cashKey.split(",") : []), [cashKey]);
276
313
  const riskWindow = useMemo(
277
314
  () => (since === "filed" ? { since: "filed" as const, weights: weights === "current" ? "current" as const : "13f" as const } : {}),
278
315
  [since, weights],
@@ -302,12 +339,12 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
302
339
  const loader = useCallback(
303
340
  async (force: boolean) => {
304
341
  const [market, brokerOptions] = await Promise.all([
305
- loadPortfolioRiskMarket(requests, force, portfolio!.currency),
342
+ loadPortfolioRiskMarket(requests, force, portfolio!.currency, cashCurrencies),
306
343
  loadPortfolioOptionBook(localTickers, portfolio!),
307
344
  ]);
308
345
  return { ...market, brokerOptions };
309
346
  },
310
- [requests, localTickers, portfolio, session.requestKey],
347
+ [requests, localTickers, portfolio, cashCurrencies, session.requestKey],
311
348
  );
312
349
  const resource = useAsyncResource(!frozen && portfolio ? loader : null, {
313
350
  clearOnError: isAccessDenied,
@@ -335,14 +372,17 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
335
372
  equity: Number(equity),
336
373
  rates: Number(rates),
337
374
  volatility: Number(volatility),
375
+ fx: shocks.fx,
376
+ assets: shocks.assets,
338
377
  },
339
378
  riskWindow,
379
+ cash,
340
380
  ),
341
381
  error: null,
342
382
  };
343
383
  } catch (error) {
344
384
  return {
345
- model: buildPortfolioRisk(portfolio, localTickers, market, null, undefined, riskWindow),
385
+ model: buildPortfolioRisk(portfolio, localTickers, market, null, undefined, riskWindow, cash),
346
386
  error: error instanceof Error ? error.message : "Evidence invalid",
347
387
  };
348
388
  }
@@ -356,7 +396,9 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
356
396
  equity,
357
397
  rates,
358
398
  volatility,
399
+ shocks,
359
400
  riskWindow,
401
+ cash,
360
402
  ]);
361
403
  const model = derived.model;
362
404
  const marketLoading = resource.loading && !resource.data;
@@ -365,8 +407,16 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
365
407
  if (!model || !BASKET_VIEWS.has(view)) return null;
366
408
  if (!frozen && !resource.data)
367
409
  return marketLoading ? { loading: true } : resource.error ? { error: resource.error } : null;
368
- return view === "holdings" ? null : riskCoverageShortfall(model.coverage);
410
+ if (view === "holdings") return null;
411
+ const shortfall = riskCoverageShortfall(model.coverage);
412
+ // The currency and price shocks reprice the book without the basket, so they stay; the footer says why the basket scenarios do not.
413
+ return shortfall && view === "stress" && model.rows.stress.some(isShockRow) ? null : shortfall;
369
414
  }, [frozen, marketLoading, model, resource.data, resource.error, view]);
415
+ // Stress with too little covered for the basket scenarios, its shocks shown alone.
416
+ const stressShortfall = useMemo(
417
+ () => (!basketBody && model && view === "stress" ? riskCoverageShortfall(model.coverage) : null),
418
+ [basketBody, model, view],
419
+ );
370
420
  const holdingsSummary = useMemo(
371
421
  () => view === "holdings" ? holdingsSummaryItems((model?.rows.holdings ?? []).filter((row) => HOLDINGS_SUMMARY_ROW_IDS.has(row.id))) : [],
372
422
  [model, view],
@@ -375,6 +425,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
375
425
  () =>
376
426
  (basketBody ? [] : model?.rows[view] ?? [])
377
427
  .filter((row) => view !== "holdings" || !HOLDINGS_SUMMARY_ROW_IDS.has(row.id))
428
+ .filter((row) => !stressShortfall || isShockRow(row))
378
429
  .sort((a, b) => {
379
430
  if (!sort.column) return 0;
380
431
  const left = a[sort.column as keyof RiskDisplayRow],
@@ -388,7 +439,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
388
439
  (sort.direction === "asc" ? 1 : -1)
389
440
  );
390
441
  }),
391
- [basketBody, model, view, sort],
442
+ [basketBody, stressShortfall, model, view, sort],
392
443
  );
393
444
  const openRow = rows.find((row) => row.id === open);
394
445
  const detailScrollRef = useRef<ScrollBoxRenderable | null>(null);
@@ -401,6 +452,8 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
401
452
  rows.length > 1 && rows.every((row) => row.detail === rows[0]!.detail)
402
453
  ? rows[0]!.detail
403
454
  : null;
455
+ // The risk rows read their window in their labels, so the footer names how the tail rows are estimated instead of a column.
456
+ const footerEvidence = (view === "risk" ? rows.find((row) => TAIL_ROW_IDS.has(row.id))?.detail : null) ?? sharedEvidence;
404
457
  const importEvidence = useCallback(async () => {
405
458
  try {
406
459
  const text = await renderer.readText(),
@@ -461,30 +514,37 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
461
514
  ...riskCoverageNotices(model?.coverage),
462
515
  ...(model?.notes ?? []),
463
516
  ...(model?.warnings ?? []),
517
+ ...shocks.errors,
464
518
  ...(derived.error ? [derived.error] : []),
465
519
  ...(resource.error ? [resource.error] : []),
466
520
  ];
467
- // What the basket views leave out, unless the body already says why they show nothing.
468
- const coverageText = model && BASKET_VIEWS.has(view) && !basketBody ? riskCoverageHeadline(model.coverage) : null;
469
- const asOfDate = model?.rows[view].find((row) => row.asOf)?.asOf?.slice(0, 10) ?? null;
521
+ // What the basket views cover of the book and leave out, unless the body already says why they show nothing.
522
+ // Stress showing its shocks alone says instead why the basket scenarios are not there.
523
+ const coverageText = !model || !BASKET_VIEWS.has(view) || basketBody
524
+ ? null
525
+ : stressShortfall
526
+ ? `Index, 10Y and VIX scenarios: ${stressShortfall.title.replace(/\.$/, "").replace(/^[A-Z](?=[a-z])/, (letter) => letter.toLowerCase())}`
527
+ : riskCoverageLine(model.coverage);
528
+ const asOfDate = (stressShortfall ? rows : model?.rows[view])?.find((row) => row.asOf)?.asOf?.slice(0, 10) ?? null;
470
529
  // The views estimated from returns name the currency a non-USD account's returns were converted into.
471
- const returnsCurrency = BASKET_VIEWS.has(view) && view !== "holdings" ? riskReturnsCurrency(model) : null;
472
- const amountBase = rows.map(riskAmountBase).find(Boolean);
530
+ const returnsCurrency = BASKET_VIEWS.has(view) && view !== "holdings" && !stressShortfall ? riskReturnsCurrency(model) : null;
473
531
  // Dated evidence only when a row has a date; a missing date is not a claim about history.
474
532
  const asOfText = !model
475
533
  ? null
476
534
  : [
477
535
  ...(basketBody ? [] : [
478
- ...(asOfDate && sharedEvidence ? [sharedEvidence] : []),
479
- // The value the tail losses in money are taken on names the currency too.
480
- ...(amountBase
481
- ? [amountBase, ...(returnsCurrency ? [returnsCurrency] : [])]
482
- : [returnsCurrency ?? model.portfolio.currency]),
483
- ...(asOfDate ? [asOfDate] : []),
536
+ ...(coverageText ? [coverageText] : []),
537
+ ...(asOfDate && footerEvidence ? [footerEvidence] : []),
538
+ // The coverage names the currency of its basket value; a non-USD account's returns say what they converted into.
539
+ ...(returnsCurrency ? [returnsCurrency] : coverageText ? [] : [model.portfolio.currency]),
484
540
  ...(EVIDENCE_VIEWS.has(view) && model.evidence ? [model.evidence.source] : []),
485
541
  ]),
486
- ...(frozen ? ["snapshot"] : []),
487
542
  ].join(" · ") || null;
543
+ // What the figures are read from and to when: the basket's daily closes, or the marks and evidence of that day.
544
+ // The shocks alone are repriced at the current marks.
545
+ const sourceText = basketBody || !asOfDate
546
+ ? null
547
+ : DAILY_CLOSE_VIEWS.has(view) && !stressShortfall ? `daily closes to ${asOfDate}` : `as of ${asOfDate}`;
488
548
  usePaneNoticeFooter({
489
549
  registrationId: "portfolio-risk-notices",
490
550
  notices,
@@ -505,12 +565,13 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
505
565
  },
506
566
  ]
507
567
  : []),
508
- // One segment, coverage first: a narrow footer cuts the date before what the numbers cover.
509
- ...(coverageText || asOfText
510
- ? [{
511
- id: "asof",
512
- parts: [{ text: [coverageText, asOfText].filter(Boolean).join(" · "), tone: "muted" as const }],
513
- }]
568
+ // Coverage first: a narrow footer cuts the method and the date before what the numbers cover.
569
+ ...(asOfText
570
+ ? [{ id: "asof", parts: [{ text: asOfText, tone: "muted" as const }] }]
571
+ : []),
572
+ // The source and its date, which a capture's own dated status line stands in for.
573
+ ...(sourceText
574
+ ? [{ id: "source", asOf: true, parts: [{ text: sourceText, tone: "muted" as const }] }]
514
575
  : []),
515
576
  ...(actionStatus
516
577
  ? [
@@ -523,7 +584,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
523
584
  ],
524
585
  hints,
525
586
  }),
526
- [resource.loading, coverageText, asOfText, actionStatus, hints],
587
+ [resource.loading, asOfText, sourceText, actionStatus, hints],
527
588
  );
528
589
  useShortcut(
529
590
  (event) => {
@@ -611,7 +672,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
611
672
  ? []
612
673
  : [{ id: "asOf", label: "As of", width: 11, align: "left" as const }]),
613
674
  // Attribution's evidence is the three effects, now columns of their own.
614
- ...(width >= 95 && !sharedEvidence && !attributionParts
675
+ ...(width >= 95 && !footerEvidence && !attributionParts
615
676
  ? [
616
677
  {
617
678
  id: "detail",
@@ -623,7 +684,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
623
684
  ]
624
685
  : []),
625
686
  ],
626
- [width, view, valueHeader, sharedUnit, attributionParts, sharedEvidence, showPercentile, sharedDate, rows],
687
+ [width, view, valueHeader, sharedUnit, attributionParts, footerEvidence, showPercentile, sharedDate, rows],
627
688
  );
628
689
  const selectView = (value: string) => {
629
690
  setView(value);
@@ -10,6 +10,7 @@ import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginAppActi
10
10
  import { useThemeColors } from "../../../theme/theme-context";
11
11
  import type { PaneProps } from "../../../types/plugin";
12
12
  import { Box, ScrollBox, Text, TextAttributes, useRendererHost, useUiCapabilities } from "../../../ui";
13
+ import { withCountryFlag } from "../../../components/ui/country-flag";
13
14
  import { SignInWall } from "../cloud/auth-actions";
14
15
  import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
15
16
  import { useOpenDescription } from "../shared/open-description";
@@ -173,13 +174,16 @@ export function AttentionPane({ width, height, focused }: PaneProps) {
173
174
  color: row.zScore >= 2 ? colors.warning : row.zScore < 0 ? colors.textDim : colors.text, attributes: row.zScore >= 2 ? TextAttributes.BOLD : 0, keepColorWhenSelected: row.zScore >= 2 };
174
175
  case "priceChangePct": return row.priceChangePct == null ? missingCell(colors) : { text: rowCell(row, column.id), value: row.priceChangePct, color: signedColor(row.priceChangePct, colors) };
175
176
  case "relativeVolume": return row.relativeVolume == null ? missingCell(colors) : { text: rowCell(row, column.id), value: row.relativeVolume, color: row.relativeVolume >= 2 ? colors.textBright : colors.text };
176
- case "sector": case "country": return { text: rowCell(row, column.id), color: colors.textDim };
177
+ case "sector": return { text: rowCell(row, column.id), color: colors.textDim };
178
+ // The column has room for the flag beside a code.
179
+ case "country": return withCountryFlag({ text: rowCell(row, column.id), color: colors.textDim }, row.country, desktop);
177
180
  default: return { text: rowCell(row, column.id), value: numeric, color: colors.text };
178
181
  }
179
182
  }
180
183
  const value = row[column.id as keyof AttentionGroup];
181
- return { text: typeof value === "number" ? number(value) : value, value: typeof value === "number" ? value : undefined,
184
+ const groupCell: DataTableCell = { text: typeof value === "number" ? number(value) : value, value: typeof value === "number" ? value : undefined,
182
185
  color: column.id === "name" ? colors.textBright : column.id === "tickers" ? colors.textDim : colors.text };
186
+ return tab === "countries" && column.id === "name" ? withCountryFlag(groupCell, row.name, desktop) : groupCell;
183
187
  };
184
188
  // The whole board in four figures; a preview would only summarise its own three rows.
185
189
  const top = (groups: readonly AttentionGroup[]) => groups[0] ? { value: groups[0].name, detail: `${number(groups[0].sharePct, 1)}%` } : null;
@@ -4,7 +4,7 @@ import type { HeadlessBundleSection, HeadlessPaneColumn, HeadlessPaneContext, He
4
4
  import { DATE_OPTION_EXAMPLES, isoDateOption } from "../../../utils/calendar-date";
5
5
  import { resolveHeadlessInstrument } from "../shared/headless-market-data";
6
6
  import { fetchAward, fetchAwards } from "./client";
7
- import { AWARD_TABS, awardScope, awardsPreviewNote, awardTypeLabel, futureAwardDatesNote, money } from "./model";
7
+ import { AWARD_TABS, awardScope, awardsCoverageNotes, awardsEmptyReason, awardsPreviewNote, awardTypeLabel, futureAwardDatesNote, money } from "./model";
8
8
  import { REPORT_PAGE, REPORT_ROWS } from "../shared/headless-options";
9
9
 
10
10
  /**
@@ -113,6 +113,8 @@ export const awardsHeadless: HeadlessPaneDefinition<"bundle"> = {
113
113
  const missing = filter.ticker && (data.locked || !data.rows.length) ? await notATicker(filter.ticker, ctx) : null;
114
114
  const lock = awardsPreviewNote(data, filter);
115
115
  const futureDates = futureAwardDatesNote(data.rows, data.generatedAt);
116
+ const notes = [...awardsCoverageNotes(data), ...(futureDates ? [futureDates] : [])];
117
+ const empty = awardsEmptyReason(data);
116
118
  const sections: HeadlessBundleSection[] = [];
117
119
  const tab = String(args.options.tab ?? "feed");
118
120
  if (tab === "feed" || tab === "company" || tab === "all") sections.push({ title: "Awards", columns: FEED_COLUMNS, rows: data.rows.map(awardRecord) });
@@ -122,8 +124,10 @@ export const awardsHeadless: HeadlessPaneDefinition<"bundle"> = {
122
124
  { title: "Sector totals", columns: aggregateColumns("Sector"), rows: data.sectors.map((row) => ({ ...row })) });
123
125
  if (tab === "events" || tab === "all") sections.push({ title: "Awards relative to annual revenue", columns: EVENT_COLUMNS, rows: data.alerts.map(awardRecord) });
124
126
  return { complete: !data.locked && !data.nextCursor && data.status === "available" && !Object.values(data.truncated ?? {}).some(Boolean), sections,
125
- ...(futureDates ? { notes: [futureDates] } : {}),
126
- errors: [...(missing ? [missing] : []), ...data.gaps, ...(lock ? [lock] : [])],
127
+ // Collection health is what the awards may be missing, a coverage note; only a failure is an error.
128
+ ...(notes.length ? { notes } : {}),
129
+ ...(empty ? { empty } : {}),
130
+ errors: [...(missing ? [missing] : []), ...(lock ? [lock] : [])],
127
131
  // A preview is the same latest awards whatever was asked, so the filters were requested, not applied.
128
132
  metadata: { generatedAt: data.generatedAt, asOf: data.asOf, access: data.access, nextCursor: data.nextCursor, truncated: data.truncated, coverage: data.coverage, sources: data.sources,
129
133
  filters: filter, filtersApplied: !data.locked } };
@@ -67,6 +67,24 @@ export function awardsPreviewNote(data: AwardsPayload, filter: AwardFilter): str
67
67
  });
68
68
  }
69
69
 
70
+ /** The server's word for a selection with no awards: the report's empty line, not a gap in coverage. */
71
+ const NO_AWARDS = "No awards are available for this selection.";
72
+
73
+ /** Why the awards asked for are empty, as the server says it; null while there are some. */
74
+ export function awardsEmptyReason(data: Pick<AwardsPayload, "gaps">): string | null {
75
+ return data.gaps.includes(NO_AWARDS) ? NO_AWARDS : null;
76
+ }
77
+
78
+ /**
79
+ * Collection health (jurisdictions awaiting a first collection, unfinished
80
+ * windows, quarantined records, a paused or capped store): what the awards
81
+ * shown may be missing, so coverage notes in the pane, `fn AWARDS` and its
82
+ * JSON, never errors.
83
+ */
84
+ export function awardsCoverageNotes(data: Pick<AwardsPayload, "gaps">): string[] {
85
+ return data.gaps.filter((gap) => gap !== NO_AWARDS);
86
+ }
87
+
70
88
  /** Award dates after the response's own date are the source's, not a mapping slip; they say so instead of passing as past awards. */
71
89
  export function futureAwardDatesNote(rows: readonly AwardRow[], generatedAt: string): string | null {
72
90
  const today = generatedAt.slice(0, 10);
@@ -12,6 +12,7 @@ import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
12
12
  import { useThemeColors } from "../../../theme/theme-context";
13
13
  import type { PaneProps } from "../../../types/plugin";
14
14
  import { Box, Text, TextAttributes, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
15
+ import { withCountryFlag } from "../../../components/ui/country-flag";
15
16
  import { publicTickerKey } from "../../../utils/exchanges";
16
17
  import { truncateToDisplayWidth } from "../../../utils/format";
17
18
  import { SignInWall } from "../cloud/auth-actions";
@@ -23,7 +24,7 @@ import { AwardDetail } from "./detail";
23
24
  import { useAwardAlerts } from "./alerts";
24
25
  import { AWARDS_UNAVAILABLE, fetchAwards, loadAward, loadAwards, validateAwards } from "./client";
25
26
  import { useAwardsEvidence } from "./evidence";
26
- import { AWARD_TABS, aggregateId, awardHistorySeries, awardPercent, awardPeriod, awardScope, awardsPreviewNote, awardTab, awardTypeLabel, futureAwardDatesNote, money,
27
+ import { AWARD_TABS, aggregateId, awardHistorySeries, awardPercent, awardPeriod, awardScope, awardsCoverageNotes, awardsPreviewNote, awardTab, awardTypeLabel, futureAwardDatesNote, money,
27
28
  rankedAggregates } from "./model";
28
29
 
29
30
  type Item = { kind: "award"; row: AwardRow } | { kind: "aggregate"; row: AwardAggregate } | { kind: "leader"; row: AwardCompanyLeader } | { kind: "locked"; id: string }
@@ -186,7 +187,8 @@ function AwardsView({ width, height, focused, symbol }: PaneProps & { symbol: st
186
187
  error: data && !denied ? failed?.message ?? null : null, stale: loaded?.stale,
187
188
  info: data?.asOf ? [{ id: "asof", parts: [{ text: `as of ${data.asOf.slice(0, 10)}`, tone: "muted" }] }] : [], hints });
188
189
  usePaneNoticeFooter({ registrationId: "awards:notices", focused, notices: [
189
- ...(data?.status === "partial" ? ["Procurement coverage is incomplete for the selected scope."] : []),
190
+ // The same coverage notes `fn AWARDS` prints: what the awards shown may be missing.
191
+ ...(data ? awardsCoverageNotes(data) : []),
190
192
  ...(loaded?.refreshError ? [loaded.refreshError] : []),
191
193
  ...(data?.truncated && Object.values(data.truncated).some(Boolean) ? ["Some aggregate history exceeds this response's limit. Narrow the date range or scope."] : []),
192
194
  ...(tab === "company" && currencies.length > 1 && !currency ? [`Chart in ${chartCurrency}; select a currency to inspect other obligations.`] : []),
@@ -269,7 +271,8 @@ function AwardsView({ width, height, focused, symbol }: PaneProps & { symbol: st
269
271
  if (column.id === "recipient") return { text: award.recipient.name, color: colors.textBright };
270
272
  if (column.id === "ticker") return award.entity ? { text: award.entity.ticker, content: <Badge label={award.entity.ticker} /> } : missingCell(colors);
271
273
  if (column.id === "agency") return { text: award.agency.name, color: colors.text };
272
- if (column.id === "country") return { text: award.jurisdiction, color: colors.textDim };
274
+ // The column has room for the flag beside a code.
275
+ if (column.id === "country") return withCountryFlag({ text: award.jurisdiction, color: colors.textDim }, award.jurisdiction, desktop);
273
276
  if (column.id === "period") return { text: awardPeriod(award), color: colors.textDim };
274
277
  if (column.id === "title") return { text: award.title, color: colors.text };
275
278
  if (column.id === "ratio") return { text: awardPercent(award), value: award.revenueComparison?.percent ?? null, color: colors.warning, attributes: TextAttributes.BOLD, keepColorWhenSelected: true };
@@ -5,7 +5,7 @@ import { resolveHeadlessInstrument } from "../shared/headless-market-data";
5
5
  import { createBacktestDependencies, loadBacktestHistory } from "./client";
6
6
  import { BACKTEST_PRESETS, DEFAULT_PRESET, lookbackYears, resolveRules } from "./presets";
7
7
  import { BACKTEST_FRESHNESS, backtestPriceUnit, backtestReport } from "./report";
8
- import { parseRule } from "./rules";
8
+ import { parseRule, RULE_SYNTAX } from "./rules";
9
9
  import { runHistoryBacktest } from "./window";
10
10
 
11
11
  /** The listing's price unit, as `history` reads it from the quote; null when no quote says. */
@@ -39,8 +39,8 @@ export const backtestHeadless: HeadlessPaneDefinition<"bundle"> = {
39
39
  options: [
40
40
  { key: "preset", type: "enum", defaultValue: DEFAULT_PRESET.id, description: "Strategy preset, or custom to use --entry and --exit",
41
41
  values: [...BACKTEST_PRESETS.map(({ id }) => ({ value: id })), { value: "custom" }] },
42
- { key: "entry", type: "string", defaultValue: "", description: "Custom entry rule, for example 'close > sma(200)'" },
43
- { key: "exit", type: "string", defaultValue: "", description: "Custom exit rule, for example 'close < sma(200)'" },
42
+ { key: "entry", type: "string", defaultValue: "", description: `Custom entry rule, for example 'close > sma(200)' or 'weekday == 3'. ${RULE_SYNTAX}` },
43
+ { key: "exit", type: "string", defaultValue: "", description: "Custom exit rule, for example 'close < sma(200)' or 'weekday == 4'. Same operands and comparisons as --entry" },
44
44
  { key: "lookback", type: "enum", defaultValue: "10", values: [{ value: "5" }, { value: "10" }, { value: "max" }], description: "Evaluation years after indicator warmup" },
45
45
  { key: "cost", type: "integer", defaultValue: 5, minimum: 0, maximum: 100, description: "Cost per side in basis points" },
46
46
  { key: "end", type: "string", defaultValue: "", placeholder: "date", example: "--end 2026-10-16 --json",
@@ -14,9 +14,11 @@ export interface BacktestBar {
14
14
  export type Operand =
15
15
  | { kind: "number"; value: number }
16
16
  | { kind: "field"; field: "close" | "open" | "high" | "low" }
17
+ | { kind: "calendar"; name: CalendarName }
17
18
  | { kind: "indicator"; name: IndicatorName; args: number[] };
18
19
  type IndicatorName = "sma" | "ema" | "rsi" | "macd" | "macd_signal" | "bb_upper" | "bb_lower" | "highest" | "lowest";
19
- type Comparator = ">" | "<" | ">=" | "<=" | "crosses above" | "crosses below";
20
+ type CalendarName = "weekday" | "day_of_month";
21
+ type Comparator = ">" | "<" | ">=" | "<=" | "==" | "!=" | "crosses above" | "crosses below";
20
22
  interface Condition { left: Operand; comparator: Comparator; right: Operand }
21
23
  /** Conditions are ANDed. */
22
24
  export type Rule = Condition[];
@@ -35,6 +37,11 @@ const INDICATORS: Record<IndicatorName, IndicatorSpec> = {
35
37
  lowest: { defaults: [20], bounds: [PERIOD], usage: "lowest(n)" },
36
38
  };
37
39
  const FIELDS = new Set(["close", "open", "high", "low"]);
40
+ /** The session's UTC calendar date as a number, so a rule can trade a day of the week or of the month. */
41
+ const CALENDAR: Record<CalendarName, { min: number; max: number; values: string; of: (date: Date) => number }> = {
42
+ weekday: { min: 1, max: 7, values: "1 Monday to 7 Sunday", of: (date) => (date.getUTCDay() + 6) % 7 + 1 },
43
+ day_of_month: { min: 1, max: 31, values: "1 to 31", of: (date) => date.getUTCDate() },
44
+ };
38
45
  const COMPARATORS: Array<[RegExp, Comparator]> = [
39
46
  [/^crosses\s+above$/, "crosses above"],
40
47
  [/^crosses\s+below$/, "crosses below"],
@@ -42,16 +49,29 @@ const COMPARATORS: Array<[RegExp, Comparator]> = [
42
49
  [/^(<|below)$/, "<"],
43
50
  [/^>=$/, ">="],
44
51
  [/^<=$/, "<="],
52
+ [/^==?$/, "=="],
53
+ [/^!=$/, "!="],
45
54
  ];
46
55
 
56
+ /** Every operand a rule takes, as an error and `--help` list them. */
57
+ const RULE_OPERANDS = [
58
+ "close", "open", "high", "low", "a number",
59
+ ...Object.entries(CALENDAR).map(([name, entry]) => `${name} (${entry.values})`),
60
+ ...Object.values(INDICATORS).map((entry) => entry.usage),
61
+ ].join(", ");
62
+ const RULE_COMPARISONS = ">, <, >=, <=, ==, !=, crosses above, crosses below";
63
+ /** What a rule is written from, for a message that sends the reader back to the syntax. */
64
+ export const RULE_SYNTAX = `Operands: ${RULE_OPERANDS}. Comparisons: ${RULE_COMPARISONS}. Join conditions with "and".`;
65
+
47
66
  function parseOperand(text: string): Operand {
48
67
  const value = text.trim().toLowerCase();
49
68
  if (/^[+-]?(\d+(\.\d*)?|\.\d+)$/.test(value)) return { kind: "number", value: Number(value) };
50
69
  if (FIELDS.has(value)) return { kind: "field", field: value as "close" };
70
+ if (Object.hasOwn(CALENDAR, value)) return { kind: "calendar", name: value as CalendarName };
51
71
  const match = /^([a-z_]+)\s*(?:\(([^)]*)\))?$/.exec(value);
52
72
  const name = match?.[1] as IndicatorName | undefined;
53
73
  const spec = name ? INDICATORS[name] : undefined;
54
- if (!match || !name || !spec) throw new Error(`Unknown operand "${text.trim()}". Use close, open, high, low, a number or ${Object.values(INDICATORS).map((entry) => entry.usage).join(", ")}.`);
74
+ if (!match || !name || !spec) throw new Error(`Unknown operand "${text.trim()}". Use ${RULE_OPERANDS}.`);
55
75
  const given = match[2]?.trim() ? match[2].split(",").map((part) => part.trim()) : [];
56
76
  if (given.length > spec.defaults.length) throw new Error(`${spec.usage} takes at most ${spec.defaults.length} values.`);
57
77
  const args = spec.defaults.map((fallback, index) => {
@@ -72,12 +92,19 @@ export function parseRule(text: string): Rule {
72
92
  const source = text.trim();
73
93
  if (!source) throw new Error("Enter a rule, for example: sma(50) crosses above sma(200)");
74
94
  return source.split(/\s+and\s+/i).map((part) => {
75
- const match = /^(.+?)\s+(crosses\s+above|crosses\s+below|>=|<=|>|<|above|below)\s+(.+)$/i.exec(part.trim());
76
- if (!match) throw new Error(`Cannot read "${part.trim()}". Write <operand> <comparison> <operand>, for example close > sma(200).`);
95
+ const match = /^(.+?)\s+(crosses\s+above|crosses\s+below|>=|<=|==|!=|=|>|<|above|below)\s+(.+)$/i.exec(part.trim());
96
+ if (!match) throw new Error(`Cannot read "${part.trim()}". Write <operand> <comparison> <operand>, for example close > sma(200). ${RULE_SYNTAX}`);
77
97
  const comparator = COMPARATORS.find(([pattern]) => pattern.test(match[2]!.toLowerCase().replace(/\s+/g, " ")))![1];
78
98
  const left = parseOperand(match[1]!);
79
99
  const right = parseOperand(match[3]!);
80
- if (left.kind === "number" && right.kind === "number") throw new Error("Compare at least one price or indicator.");
100
+ if (left.kind === "number" && right.kind === "number") throw new Error("Compare at least one price, date or indicator.");
101
+ for (const [calendar, other] of [[left, right], [right, left]] as const) {
102
+ if (calendar.kind !== "calendar" || other.kind !== "number") continue;
103
+ const { min, max, values } = CALENDAR[calendar.name];
104
+ if (!Number.isInteger(other.value) || other.value < min || other.value > max) {
105
+ throw new Error(`${calendar.name} is ${values}, not ${other.value}.`);
106
+ }
107
+ }
81
108
  return { left, comparator, right };
82
109
  });
83
110
  }
@@ -105,6 +132,13 @@ export function evaluateOperand(operand: Operand, bars: readonly BacktestBar[]):
105
132
  const length = bars.length;
106
133
  if (operand.kind === "number") return new Float64Array(length).fill(operand.value);
107
134
  if (operand.kind === "field") return Float64Array.from(bars, (bar) => bar[operand.field] ?? Number.NaN);
135
+ if (operand.kind === "calendar") {
136
+ // A session's date is its UTC calendar day; a bar without a readable one has no weekday.
137
+ return Float64Array.from(bars, (bar) => {
138
+ const time = Date.parse(`${bar.date.slice(0, 10)}T00:00:00Z`);
139
+ return Number.isFinite(time) ? CALENDAR[operand.name].of(new Date(time)) : Number.NaN;
140
+ });
141
+ }
108
142
  const closes = bars.map((bar) => bar.close);
109
143
  const [a = 0, b = 0, c = 0] = operand.args;
110
144
  switch (operand.name) {
@@ -161,6 +195,8 @@ export function evaluateRule(rule: Rule, bars: readonly BacktestBar[]): boolean[
161
195
  else if (condition.comparator === "<") met = l < r;
162
196
  else if (condition.comparator === ">=") met = l >= r;
163
197
  else if (condition.comparator === "<=") met = l <= r;
198
+ else if (condition.comparator === "==") met = l === r;
199
+ else if (condition.comparator === "!=") met = l !== r;
164
200
  else {
165
201
  const pl = left[index - 1], pr = right[index - 1];
166
202
  met = pl !== undefined && pr !== undefined && !Number.isNaN(pl) && !Number.isNaN(pr)
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore
2
2
  import { apiClient } from "../../../api-client";
3
3
  import {
4
4
  brokerMethodLabel,
5
+ brokerDirectorySummary,
5
6
  brokerMethodSummary,
6
7
  buildBrokerDirectory,
7
8
  type BrokerDirectoryEntry,
@@ -39,7 +40,7 @@ import { isPlainKey } from "../../../utils/keyboard";
39
40
  import { usePluginAppActions, usePluginBrokerActions } from "../../runtime";
40
41
  import { BrokerProfileForm, profileFieldWidth, type BrokerEditKey } from "./detail";
41
42
  import { useBrokerManagerKeyboard } from "./keyboard";
42
- import type { BrokerManagerMessage } from "./pane-actions";
43
+ import type { BrokerManagerMessage, BrokerRowNote } from "./pane-actions";
43
44
 
44
45
  /**
45
46
  * Adding a profile, one step at a time in the pane's detail: the broker, how
@@ -84,10 +85,13 @@ let attemptSequence = 0;
84
85
  export function useBrokerAddFlow({
85
86
  setBusy,
86
87
  setMessage,
88
+ setRowNote,
87
89
  onProfileAdded,
88
90
  }: {
89
91
  setBusy: (busy: string | null) => void;
90
92
  setMessage: (message: BrokerManagerMessage | null) => void;
93
+ /** Says on the new profile's row why its first sync failed. */
94
+ setRowNote: (profileId: string, note: BrokerRowNote | null) => void;
91
95
  /** Lands the pane on the profile the flow made, with the flow closed. */
92
96
  onProfileAdded: (instanceId: string) => void;
93
97
  }) {
@@ -95,7 +99,7 @@ export function useBrokerAddFlow({
95
99
  const dispatch = useAppDispatch();
96
100
  const getState = useAppGetState();
97
101
  const { notify } = usePluginAppActions();
98
- const { listBrokerAdapters, createBrokerInstance, syncBrokerInstance } = usePluginBrokerActions();
102
+ const { listBrokerAdapters, createBrokerInstance, updateBrokerInstance, syncBrokerInstance } = usePluginBrokerActions();
99
103
  const [flow, setFlowState] = useState<BrokerAddFlowState | null>(null);
100
104
  // Async work reads the flow as it is now, not as it was when it started.
101
105
  const flowRef = useRef<BrokerAddFlowState | null>(null);
@@ -165,13 +169,13 @@ export function useBrokerAddFlow({
165
169
  if (portfolio) showCollectionInPortfolioPane(state, dispatch, portfolio.id);
166
170
  notify({ body: t("Connected! Positions will sync automatically."), type: "success" });
167
171
  } catch (error) {
168
- // The profile stays: its detail and the footer say why it did not sync.
172
+ // The profile stays: its row says why it did not sync.
169
173
  land();
170
- setMessage({ tone: "error", text: errorText(error, tf("Failed to sync {label}.", { label })) });
174
+ setRowNote(instanceId, { tone: "error", status: t("Sync failed"), text: errorText(error, tf("Failed to sync {label}.", { label })) });
171
175
  } finally {
172
176
  setBusy(null);
173
177
  }
174
- }, [dispatch, getState, notify, onProfileAdded, setBusy, setFlow, setMessage, syncBrokerInstance]);
178
+ }, [dispatch, getState, notify, onProfileAdded, setBusy, setFlow, setRowNote, syncBrokerInstance]);
175
179
 
176
180
  const connectDevice = useCallback(async () => {
177
181
  const current = flowRef.current;
@@ -240,6 +244,7 @@ export function useBrokerAddFlow({
240
244
  connected = await connectSignedInBrokerProfile(broker, {
241
245
  getConfig: () => getState().config,
242
246
  createBrokerInstance,
247
+ enableBrokerInstance: (instanceId) => updateBrokerInstance(instanceId, {}, { enabled: true }),
243
248
  // The first sync runs below, with its progress in the pane.
244
249
  syncBrokerInstance: async () => {},
245
250
  // Gloom's sign-in over the pane when needed, again after a session Gloom refused.
@@ -278,7 +283,7 @@ export function useBrokerAddFlow({
278
283
  } finally {
279
284
  if (committing) committingRef.current = false;
280
285
  }
281
- }, [createBrokerInstance, dialog, getState, setMessage, setStep, showAttempt, syncNewProfile]);
286
+ }, [createBrokerInstance, dialog, getState, setMessage, setStep, showAttempt, syncNewProfile, updateBrokerInstance]);
282
287
 
283
288
  const chooseMethod = useCallback((entry: BrokerDirectoryEntry, method: BrokerMethod) => {
284
289
  const current = flowRef.current;
@@ -384,7 +389,7 @@ export function BrokerAddFlowContent({
384
389
  label: entry.name,
385
390
  // The desktop says how it connects under the name, beside its logo.
386
391
  ...(desktop
387
- ? { description: brokerMethodSummary(entry), leading: <LogoMark src={brokerDirectoryLogo(entry, getBrokerLogo)} name={entry.name} size="lead" /> }
392
+ ? { description: brokerDirectorySummary(entry), leading: <LogoMark src={brokerDirectoryLogo(entry, getBrokerLogo)} name={entry.name} size="lead" /> }
388
393
  : { detail: brokerMethodSummary(entry) }),
389
394
  }))}
390
395
  rowDescriptions={desktop}