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
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../../../utils/log-failure";
1
2
  import { matchesNumberStep } from "../../../components/ui/number-step";
2
3
  import type { BrokerAdapter, BrokerConnectionStatus } from "../../../types/broker";
3
4
  import type { BrokerInstanceConfig } from "../../../types/config";
@@ -5,6 +6,7 @@ import type { Quote } from "../../../types/financials";
5
6
  import type { BrokerAccount, BrokerOrder, BrokerOrderPreview, BrokerOrderRequest, BrokerTradingCapabilities } from "../../../types/trading";
6
7
  import { optionContractIdentity } from "../../../utils/options";
7
8
  import { isStrategyOrder, strategyQuote } from "./strategy";
9
+ import { checkLinkedOrder, restingEntryPrice } from "./linked-orders";
8
10
  import { brokerOrderDone, brokerOrderStatus, orderEstimatePrice, ticketEditingPriceStep } from "./orders-data";
9
11
 
10
12
  export interface BrokerTradingContext {
@@ -92,6 +94,8 @@ function refusal(error: unknown): string | undefined {
92
94
  return error.message.trim().replace(/\.$/, "") || "The broker refused this action";
93
95
  }
94
96
 
97
+ /** Terms the broker sets with its figures, or for having none, that the user accepts on the review: never skipped. */
98
+ function asksForConsent(preview: BrokerOrderPreview): boolean { return !!preview.disclosure || preview.impactSource === "unavailable"; }
95
99
  function copy<T>(value: T): T { return structuredClone(value); }
96
100
  function freeze<T>(value: T): T {
97
101
  if (value && typeof value === "object") {
@@ -226,7 +230,9 @@ export class BrokerTradingController {
226
230
  this.editable();
227
231
  const original = this.modifyOrder;
228
232
  if (original && (canonical(request.contract) !== canonical(original.contract) || canonical(request.legs ?? null) !== canonical(original.legs ?? null)
229
- || request.action !== original.action || request.accountId !== original.accountId || request.orderType !== original.orderType || request.tif !== original.tif)) {
233
+ || request.action !== original.action || request.accountId !== original.accountId || request.orderType !== original.orderType || request.tif !== original.tif
234
+ // One order of a bracket or an OCO pair changes alone; a trailing stop keeps its unit.
235
+ || request.orderClass !== undefined || (request.trailingPercent !== undefined) !== (original.trailingPercent !== undefined))) {
230
236
  throw new Error("A modification keeps the original account, instrument, legs, side, type and time in force.");
231
237
  }
232
238
  // A spread's legs are its instrument: other legs need another quote.
@@ -275,15 +281,20 @@ export class BrokerTradingController {
275
281
  if (request.outsideRth && (!capabilities.extendedHours || (capabilities.extendedHoursTif && !capabilities.extendedHoursTif.includes(request.tif ?? "DAY")))) throw new Error("Extended hours are unavailable for this order and time in force.");
276
282
  const limit = request.orderType === "LMT" || request.orderType === "STP LMT";
277
283
  const stop = request.orderType === "STP" || request.orderType === "STP LMT";
278
- for (const [needed, value, label] of [[limit, request.limitPrice, "limit"], [stop, request.stopPrice, "stop"]] as const) {
279
- if (needed && !positive(value)) throw new Error(`Enter a valid ${label} price.`);
280
- if (!needed && value !== undefined) throw new Error(`Remove the ${label} price for this order type.`);
284
+ const checkPrice = (value: number) => {
281
285
  const decimals = capabilities.priceDecimals;
282
- if (value !== undefined && decimals !== undefined && (!Number.isInteger(decimals) || decimals < 0 || decimals > 18 || Number(value.toFixed(decimals)) !== value)) {
286
+ if (decimals !== undefined && (!Number.isInteger(decimals) || decimals < 0 || decimals > 18 || Number(value.toFixed(decimals)) !== value)) {
283
287
  throw new Error("Price precision exceeds the broker's limit.");
284
288
  }
285
- if (value !== undefined && capabilities.priceStep !== undefined && !matchesNumberStep(value, capabilities.priceStep)) throw new Error("Price does not match the broker's allowed increment.");
289
+ if (capabilities.priceStep !== undefined && !matchesNumberStep(value, capabilities.priceStep)) throw new Error("Price does not match the broker's allowed increment.");
290
+ };
291
+ for (const [needed, value, label] of [[limit, request.limitPrice, "limit"], [stop, request.stopPrice, "stop"]] as const) {
292
+ if (needed && !positive(value)) throw new Error(`Enter a valid ${label} price.`);
293
+ if (!needed && value !== undefined) throw new Error(`Remove the ${label} price for this order type.`);
294
+ if (value !== undefined) checkPrice(value);
286
295
  }
296
+ // A bracket, an OCO pair or a trailing stop only where the broker runs it itself.
297
+ checkLinkedOrder(request, capabilities, context.adapter.describeInstance?.(context.instance).brokerName ?? context.adapter.name, checkPrice);
287
298
  return copy(request);
288
299
  }
289
300
 
@@ -355,7 +366,9 @@ export class BrokerTradingController {
355
366
  // The stream answered what a quote request could not.
356
367
  const answered = this.snapshot.error !== undefined && this.snapshot.error === this.quoteError;
357
368
  this.publish({ quote: value, loadingQuote: false, ...(answered ? { error: undefined } : {}) });
358
- } catch { /* A quote that fails the checks leaves the last good one. */ }
369
+ } catch {
370
+ // Intentional ignore: invalid streamed quotes leave the last validated ticket quote in place.
371
+ }
359
372
  }
360
373
 
361
374
  /** A streamed quote for the current draft, still being pushed, stands in for asking. */
@@ -437,9 +450,9 @@ export class BrokerTradingController {
437
450
  this.entryPending = true;
438
451
  try {
439
452
  await this.prepareReview(fast);
440
- // New risk information cannot be waived by a session preference.
453
+ // New risk information cannot be waived by a session preference, nor can the broker's terms.
441
454
  const review = this.snapshot.review;
442
- if (fast && review && !review.requiresTypedConfirmation && !review.warnings.length && !review.preview.errors?.length) await this.submitReviewed(undefined, true);
455
+ if (fast && review && !review.requiresTypedConfirmation && !review.warnings.length && !review.preview.errors?.length && !asksForConsent(review.preview)) await this.submitReviewed(undefined, true);
443
456
  } finally { this.entryPending = false; }
444
457
  }
445
458
 
@@ -473,6 +486,19 @@ export class BrokerTradingController {
473
486
  && (request.action === "SELL" ? request.stopPrice >= touch : request.stopPrice <= touch)) {
474
487
  warnings.push(request.action === "SELL" ? "This sell stop is at or above the bid, so it triggers at once." : "This buy stop is at or below the ask, so it triggers at once.");
475
488
  }
489
+ // An exit already through the market trades the moment it works: an OCO's at once, a bracket's once the entry fills.
490
+ // A bracket's exits trade the other way from its entry; an OCO's two orders trade its own way. An entry resting away
491
+ // from the market fills at its own price, and its exits were checked against that: only one that trades now, at
492
+ // the market, can leave an exit through it.
493
+ const exitSells = request.orderClass === "oco" ? request.action === "SELL" : request.action === "BUY";
494
+ const exitTouch = request.orderClass === "bracket" && restingEntryPrice(request, quote.quote) !== undefined ? undefined
495
+ : exitSells ? quote.quote.bid : quote.quote.ask;
496
+ if (request.orderClass && positive(exitTouch) && positive(request.stopLossPrice) && (exitSells ? request.stopLossPrice >= exitTouch : request.stopLossPrice <= exitTouch)) {
497
+ warnings.push(request.orderClass === "oco" ? `This ${exitSells ? "sell" : "buy"} stop is through the market, so it triggers at once.` : "The stop loss is through the market, so it triggers as soon as the entry fills.");
498
+ }
499
+ if (request.orderClass === "bracket" && positive(exitTouch) && positive(request.takeProfitPrice) && (exitSells ? request.takeProfitPrice <= exitTouch : request.takeProfitPrice >= exitTouch)) {
500
+ warnings.push("The take profit is through the market, so it fills as soon as the entry does.");
501
+ }
476
502
  if (quote.quote.marketState === "CLOSED") warnings.push("The broker reports the market is closed. The order may wait for another session.");
477
503
  // Legs whose quotes net the other way: a debit order that would be paid, or a credit order that would pay.
478
504
  if (request.legs?.length && Number.isFinite(quote.quote.price) && quote.quote.price <= 0) warnings.push(request.action === "BUY"
@@ -482,7 +508,7 @@ export class BrokerTradingController {
482
508
  const result: BrokerOrderReview = { request, preview: copy(preview), warnings: [...new Set(warnings)], account: copy(account), brokerName: context.adapter.name,
483
509
  createdAt: this.now(), mode: this.modifyOrder ? "modify" : "place", requiresTypedConfirmation: account.tradingMode !== "simulation", confirmationText: request.contract.symbol.toUpperCase() };
484
510
  this.gate = { context: fingerprint, request: canonical(request), version, quoteRisk: this.quoteRisk(quote), order: this.modifyOrder ? copy(this.modifyOrder) : undefined };
485
- this.publish({ phase: skipConfirmation && !result.warnings.length && !preview.errors?.length ? "previewing" : "review", review: result, quote, error: preview.errors?.length ? "The broker preview has blocking errors." : undefined });
511
+ this.publish({ phase: skipConfirmation && !result.warnings.length && !preview.errors?.length && !asksForConsent(preview) ? "previewing" : "review", review: result, quote, error: preview.errors?.length ? "The broker preview has blocking errors." : undefined });
486
512
  } catch (error) {
487
513
  if (!this.disposed && version === this.version) this.publish({ phase: "editing", review: undefined, error: errorMessage(error, "The broker could not preview this order.") });
488
514
  throw error;
@@ -537,7 +563,7 @@ export class BrokerTradingController {
537
563
  const { context, capabilities } = this.context(true, request);
538
564
  this.checkOrder(order, context, "modify");
539
565
  if (!capabilities.modify || !context.adapter.modifyOrder) throw new Error("This broker does not support modifying this order.");
540
- if (!["MKT", "LMT", "STP", "STP LMT"].includes(order.orderType)) throw new Error("This order type cannot be modified in the shared ticket.");
566
+ if (!["MKT", "LMT", "STP", "STP LMT", "TRAIL"].includes(order.orderType)) throw new Error("This order type cannot be modified in the shared ticket.");
541
567
  this.modifyOrder = undefined;
542
568
  this.setDraft({ ...request, brokerInstanceId: context.instance.id });
543
569
  this.modifyOrder = copy(order);
@@ -561,7 +587,10 @@ export class BrokerTradingController {
561
587
 
562
588
  private orderRequest(order: BrokerOrder): BrokerOrderRequest {
563
589
  return { brokerInstanceId: order.brokerInstanceId, accountId: order.accountId, contract: copy(order.contract), action: order.action,
564
- orderType: order.orderType as BrokerOrderRequest["orderType"], quantity: order.quantity, limitPrice: order.limitPrice, stopPrice: order.stopPrice, tif: order.tif,
590
+ orderType: order.orderType as BrokerOrderRequest["orderType"], quantity: order.quantity, tif: order.tif,
591
+ // A trailing stop's trigger is the broker's to move: a change sets its distance.
592
+ ...(order.orderType === "TRAIL" ? order.trailingPercent !== undefined ? { trailingPercent: order.trailingPercent } : { trailingAmount: order.trailingAmount }
593
+ : { limitPrice: order.limitPrice, stopPrice: order.stopPrice }),
565
594
  ...(order.legs?.length ? { legs: copy(order.legs) } : {}) };
566
595
  }
567
596
 
@@ -608,7 +637,10 @@ export class BrokerTradingController {
608
637
  : await context.adapter.listOpenOrders?.(context.instance) ?? [];
609
638
  return orders.find((value): value is BrokerOrder => !!value && value.accountId === order.accountId
610
639
  && value.contract.symbol === order.contract.symbol && (value.orderId === order.orderId || sameOrder(value, order)));
611
- } catch { return undefined; }
640
+ } catch (error) {
641
+ logFailure("broker-trading.controller", "Look up broker order failed")(error);
642
+ return undefined;
643
+ }
612
644
  }
613
645
 
614
646
  /**
@@ -1,4 +1,4 @@
1
- import { startTransition, useCallback, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from "react";
1
+ import { startTransition, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from "react";
2
2
  import { PaneFooterScope, usePaneMenuItems } from "../../../components";
3
3
  import { usePaneAppConfig, usePaneStateValue } from "../../../state/app/context";
4
4
  import { usePluginBrokerActions } from "../../runtime";
@@ -91,15 +91,33 @@ export function ResearchTradeWorkspace({ width, height, focused, symbol, tabId,
91
91
  const ticketFocused = split && panelFocused;
92
92
  // The chart consumes its own presses, so the box below never hears them: it says so itself.
93
93
  const focusChart = useCallback(() => setPanelFocused(false), []);
94
- const context = useMemo(() => ({ ...projection, symbol, available, docked, visible, ticketFocused, open: openTicket, close, focusChart, publish, ...(available ? { orders } : {}) }),
95
- [projection, symbol, available, docked, visible, ticketFocused, openTicket, close, focusChart, orders]);
94
+ // A price picked on the chart beside the ticket: the chart takes the keys until it is picked or cancelled.
95
+ const [pickingField, setPickingField] = useState<string>();
96
+ const picking = split ? projection.priceFields?.find((entry) => entry.field === pickingField) : undefined;
97
+ const pickPrice = useCallback((field?: string) => {
98
+ const choice = field ? projection.priceFields?.find((entry) => entry.field === field) : projection.priceFields?.[0];
99
+ if (!split || !choice) return;
100
+ captureTicket(false); setPickingField(choice.field); setPanelFocused(false);
101
+ }, [split, projection.priceFields, captureTicket]);
102
+ const cancelPick = useCallback(() => { setPickingField(undefined); setPanelFocused(true); }, []);
103
+ const finishPick = useCallback((value: number) => {
104
+ if (picking) projection.setPrice?.(picking.field, value);
105
+ cancelPick();
106
+ }, [picking, projection, cancelPick]);
107
+ // Back in the ticket, away from the pane, or a ticket that no longer has that price: the pick is over.
108
+ useEffect(() => { if (pickingField && (!focused || ticketFocused || !picking)) setPickingField(undefined); }, [pickingField, focused, ticketFocused, picking]);
109
+ const context = useMemo(() => ({ ...projection, symbol, available, docked, visible, ticketFocused, open: openTicket, close, focusChart, publish,
110
+ picking, pickPrice, finishPick, cancelPick, ...(available ? { orders } : {}) }),
111
+ [projection, symbol, available, docked, visible, ticketFocused, openTicket, close, focusChart, orders, picking, pickPrice, finishPick, cancelPick]);
96
112
  useShortcut((event) => {
97
113
  if (event.name !== "f6" || event.repeat) return;
98
114
  event.preventDefault(); event.stopPropagation(); captureTicket(false); setPanelFocused((value) => !value);
99
115
  }, { enabled: focused && split && !dialogOpen, allowEditable: true, phase: "before", scope: "research:trade-focus" });
100
116
  useShortcut((event) => {
101
117
  if (event.name !== "escape" || event.defaultPrevented || event.targetEditable) return;
102
- event.preventDefault(); event.stopPropagation(); close();
118
+ event.preventDefault(); event.stopPropagation();
119
+ // While the chart picks a price, Esc only ends the pick, here too when the chart has nothing drawn to take it.
120
+ if (pickingField) cancelPick(); else close();
103
121
  }, { enabled: focused && split && !panelFocused && !dialogOpen, phase: "after", scope: "research:trade-close" });
104
122
 
105
123
  // The divider between chart and ticket drags, and < > move it from the keyboard.
@@ -1,6 +1,7 @@
1
1
  import type { BrokerAdapter } from "../../../types/broker";
2
2
  import type { HeadlessPaneContext, HeadlessPaneDefinition, HeadlessPaneColumn } from "../../../types/headless";
3
3
  import { brokerOrderStatus, loadBrokerOrdersSnapshot } from "./orders-data";
4
+ import { accountsShownFor } from "../../../brokers/broker-portfolio-sync";
4
5
 
5
6
  type BrokerResolver = (brokerType: string, context: HeadlessPaneContext) => BrokerAdapter | null;
6
7
 
@@ -44,7 +45,7 @@ function createBrokerOrdersHeadless(
44
45
  if (!profiles.length) throw new Error("Connect a broker profile that reports orders in Brokers first.");
45
46
  const { instance, broker } = profiles[ordinal(args.options.profile, profiles.length, "profile")]!;
46
47
  if (!broker.listAccounts) throw new Error("This broker does not report account identities.");
47
- const accounts = await broker.listAccounts(instance);
48
+ const accounts = accountsShownFor(context.config, instance, await broker.listAccounts(instance));
48
49
  context.signal.throwIfAborted();
49
50
  if (!accounts.length) throw new Error("This broker profile has no available accounts.");
50
51
  const account = accounts[ordinal(args.options.account, accounts.length, "account")]!;
@@ -5,7 +5,11 @@ import { brokerCanTrade, createTradeCommand, openTradeTicket } from "./command";
5
5
  import { brokerOrderFeed } from "./order-feed";
6
6
  import { tickerSecType } from "./pane-model";
7
7
 
8
- /** Every enabled trading profile's orders, held for as long as the app runs: its fills toast with no ticket open. */
8
+ /**
9
+ * Every enabled trading profile's orders, held for as long as the app runs:
10
+ * its fills toast with no ticket open, and sync the profile's positions into
11
+ * its portfolio.
12
+ */
9
13
  let releaseHeldOrders: (() => void) | undefined;
10
14
 
11
15
  /** A ticker without a saved asset category trades as its research instrument kind says. */
@@ -28,6 +32,7 @@ export const brokerTradingModule: PluginModule = {
28
32
  }],
29
33
  setup(ctx) {
30
34
  brokerOrderFeed.setNotifier((notification) => { ctx.notify(notification); });
35
+ brokerOrderFeed.setPositionSync((instanceId) => ctx.syncBrokerInstance(instanceId));
31
36
  let held = new Map<string, { key: string; release(): void }>();
32
37
  // A broker registered by a later plugin, or a profile added or edited, is held from then on.
33
38
  const holdProfiles = () => {
@@ -50,6 +55,7 @@ export const brokerTradingModule: PluginModule = {
50
55
  for (const entry of held.values()) entry.release();
51
56
  held.clear();
52
57
  brokerOrderFeed.setNotifier(undefined);
58
+ brokerOrderFeed.setPositionSync(undefined);
53
59
  brokerOrderFeed.clear();
54
60
  };
55
61
  const supported = (symbol: string, secType = "STK") => brokerCanTrade(ctx.getConfig().brokerInstances, (type) => ctx.getBrokerAdapter?.(type), symbol, secType);
@@ -0,0 +1,148 @@
1
+ import type { Quote } from "../../../types/financials";
2
+ import type { BrokerOrder, BrokerOrderClass, BrokerOrderLinkRole, BrokerOrderRequest, BrokerTradingCapabilities, BrokerTrailingUnit } from "../../../types/trading";
3
+
4
+ /**
5
+ * Brackets, OCO pairs and trailing stops: what a broker takes, what a draft of
6
+ * one must hold, and how the orders it becomes are listed together. The broker
7
+ * places and runs them; nothing here makes one out of separate orders.
8
+ */
9
+
10
+ export type LinkedFeature = BrokerOrderClass | "trailing";
11
+
12
+ const FEATURE_NAME: Record<LinkedFeature, string> = { bracket: "brackets", oco: "OCO orders", trailing: "trailing stops" };
13
+
14
+ /** Whether the broker takes a bracket, an OCO pair or a trailing stop here, and the refusal when it does not. */
15
+ export function linkedFeature(capabilities: BrokerTradingCapabilities | undefined, feature: LinkedFeature, brokerName: string): { available: boolean; reason?: string } {
16
+ const available = feature === "trailing"
17
+ ? !!capabilities?.orderTypes.includes("TRAIL") && !!capabilities.trailing?.length
18
+ : !!capabilities?.orderClasses?.includes(feature);
19
+ if (available) return { available };
20
+ return { available, reason: `${brokerName} does not take ${FEATURE_NAME[feature]}.` };
21
+ }
22
+
23
+ /** The trailing unit a draft edits: percent once it holds one, else the amount. */
24
+ export function trailingUnit(request: Pick<BrokerOrderRequest, "trailingAmount" | "trailingPercent">): BrokerTrailingUnit {
25
+ return "trailingPercent" in request && !("trailingAmount" in request) ? "percent" : "amount";
26
+ }
27
+
28
+ const positive = (value: number | undefined): value is number => value !== undefined && Number.isFinite(value) && value > 0;
29
+
30
+ /**
31
+ * The entry's own price an exit is measured against: its limit, or its stop,
32
+ * or none for a market order.
33
+ */
34
+ function entryPrice(request: BrokerOrderRequest): number | undefined {
35
+ if (request.orderType === "LMT" || request.orderType === "STP LMT") return request.limitPrice;
36
+ if (request.orderType === "STP") return request.stopPrice;
37
+ return undefined;
38
+ }
39
+
40
+ /**
41
+ * The entry's price when it rests away from the market, where it fills: a buy
42
+ * limit under the ask or a buy stop over it, a sell the mirror. None for one
43
+ * that trades now, or without a quote.
44
+ */
45
+ export function restingEntryPrice(request: BrokerOrderRequest, quote: Pick<Quote, "bid" | "ask">): number | undefined {
46
+ const buy = request.action === "BUY";
47
+ const touch = buy ? quote.ask : quote.bid;
48
+ if (!positive(touch)) return undefined;
49
+ const entry = entryPrice(request);
50
+ if (request.orderType === "LMT") return positive(entry) && (buy ? entry < touch : entry > touch) ? entry : undefined;
51
+ if (request.orderType === "STP" || request.orderType === "STP LMT") {
52
+ return positive(request.stopPrice) && (buy ? request.stopPrice > touch : request.stopPrice < touch) ? entry : undefined;
53
+ }
54
+ return undefined;
55
+ }
56
+
57
+ /**
58
+ * Checks a draft's bracket, OCO pair or trailing stop against what the broker
59
+ * declared, before any preview. Throws with the reason to show.
60
+ */
61
+ export function checkLinkedOrder(request: BrokerOrderRequest, capabilities: BrokerTradingCapabilities, brokerName: string, checkPrice: (value: number, label: string) => void): void {
62
+ const buy = request.action === "BUY";
63
+ const trailing = request.orderType === "TRAIL";
64
+ if (!trailing && (request.trailingAmount !== undefined || request.trailingPercent !== undefined)) throw new Error("Remove the trailing distance for this order type.");
65
+ if (trailing) {
66
+ const feature = linkedFeature(capabilities, "trailing", brokerName);
67
+ if (!feature.available) throw new Error(feature.reason);
68
+ const unit = trailingUnit(request);
69
+ if ((request.trailingAmount !== undefined) === (request.trailingPercent !== undefined)) throw new Error("Enter the trailing distance in price or in percent.");
70
+ if (!capabilities.trailing?.includes(unit)) throw new Error(unit === "percent" ? `${brokerName} takes a trailing stop in price, not percent.` : `${brokerName} takes a trailing stop in percent, not price.`);
71
+ const distance = unit === "percent" ? request.trailingPercent : request.trailingAmount;
72
+ if (!positive(distance) || unit === "percent" && distance >= 100) throw new Error("Enter a valid trailing distance.");
73
+ if (unit === "amount") checkPrice(distance, "trailing");
74
+ }
75
+ const exits = request.takeProfitPrice !== undefined || request.stopLossPrice !== undefined;
76
+ if (!request.orderClass) {
77
+ if (exits) throw new Error("Remove the take profit and stop loss, or make the order a bracket.");
78
+ return;
79
+ }
80
+ const feature = linkedFeature(capabilities, request.orderClass, brokerName);
81
+ if (!feature.available) throw new Error(feature.reason);
82
+ if (request.legs?.length) throw new Error("A spread cannot carry a bracket or an OCO pair.");
83
+ if (request.orderClass === "oco") {
84
+ if (request.orderType !== "LMT") throw new Error("An OCO pair is a limit and a stop.");
85
+ if (request.takeProfitPrice !== undefined) throw new Error("An OCO pair is a limit and a stop, with no take profit.");
86
+ if (!positive(request.stopLossPrice)) throw new Error("Enter a valid stop price.");
87
+ checkPrice(request.stopLossPrice, "stop");
88
+ // A sell pair exits a long: a limit above, a stop below. A buy pair is the mirror.
89
+ if (positive(request.limitPrice) && (buy ? request.limitPrice >= request.stopLossPrice : request.limitPrice <= request.stopLossPrice)) {
90
+ throw new Error(buy ? "A buy OCO's limit sits below its stop." : "A sell OCO's limit sits above its stop.");
91
+ }
92
+ return;
93
+ }
94
+ if (!["MKT", "LMT", "STP", "STP LMT"].includes(request.orderType)) throw new Error("A bracket's entry is a market, limit or stop order.");
95
+ if (!positive(request.takeProfitPrice) && !positive(request.stopLossPrice)) throw new Error("Enter a take profit, a stop loss or both.");
96
+ for (const [value, label] of [[request.takeProfitPrice, "take profit"], [request.stopLossPrice, "stop loss"]] as const) {
97
+ if (value === undefined) continue;
98
+ if (!positive(value)) throw new Error(`Enter a valid ${label} price.`);
99
+ checkPrice(value, label);
100
+ }
101
+ // A buy's take profit sells above where it bought and its stop loss below; a sell's the mirror.
102
+ const entry = entryPrice(request);
103
+ const above = (value: number, than: number) => buy ? value > than : value < than;
104
+ if (positive(entry) && positive(request.takeProfitPrice) && !above(request.takeProfitPrice, entry)) throw new Error(buy ? "The take profit must be above the entry." : "The take profit must be below the entry.");
105
+ if (positive(entry) && positive(request.stopLossPrice) && !above(entry, request.stopLossPrice)) throw new Error(buy ? "The stop loss must be below the entry." : "The stop loss must be above the entry.");
106
+ if (positive(request.takeProfitPrice) && positive(request.stopLossPrice) && !above(request.takeProfitPrice, request.stopLossPrice)) {
107
+ throw new Error(buy ? "The take profit must be above the stop loss." : "The take profit must be below the stop loss.");
108
+ }
109
+ }
110
+
111
+ export const linkRoleName: Record<BrokerOrderLinkRole, string> = { entry: "Entry", takeProfit: "Take profit", stopLoss: "Stop loss" };
112
+ /** Beside a price in a one-line row. */
113
+ export const linkRoleShort: Record<BrokerOrderLinkRole, string> = { entry: "", takeProfit: "TP", stopLoss: "SL" };
114
+
115
+ /** "Trailing $1.50", "Trailing 2%": a trailing stop's distance. */
116
+ export function trailingText(order: Pick<BrokerOrder, "trailingAmount" | "trailingPercent">, money: (value: number) => string): string {
117
+ if (positive(order.trailingPercent)) return `Trailing ${order.trailingPercent.toLocaleString("en-US", { maximumFractionDigits: 4 })}%`;
118
+ return positive(order.trailingAmount) ? `Trailing ${money(order.trailingAmount)}` : "Trailing";
119
+ }
120
+
121
+ const ROLE_ORDER: Record<BrokerOrderLinkRole, number> = { entry: 0, takeProfit: 1, stopLoss: 2 };
122
+
123
+ /**
124
+ * Orders as a list shows them: newest first, with each group's orders
125
+ * together where its newest one sits, the entry before its take profit and
126
+ * stop loss. `child` marks an exit listed under its entry.
127
+ */
128
+ export function linkedOrderRows<T extends Pick<BrokerOrder, "linkGroup" | "linkRole" | "accountId">>(orders: readonly T[]): Array<{ order: T; child: boolean }> {
129
+ const groups = new Map<string, T[]>();
130
+ for (const order of orders) {
131
+ if (!order.linkGroup) continue;
132
+ const key = `${order.accountId ?? ""}:${order.linkGroup}`;
133
+ groups.set(key, [...groups.get(key) ?? [], order]);
134
+ }
135
+ const rows: Array<{ order: T; child: boolean }> = [];
136
+ const placed = new Set<string>();
137
+ for (const order of orders) {
138
+ const key = order.linkGroup ? `${order.accountId ?? ""}:${order.linkGroup}` : undefined;
139
+ const group = key ? groups.get(key) : undefined;
140
+ if (!key || !group || group.length < 2) { rows.push({ order, child: false }); continue; }
141
+ if (placed.has(key)) continue;
142
+ placed.add(key);
143
+ const sorted = [...group].sort((a, b) => ROLE_ORDER[a.linkRole ?? "entry"] - ROLE_ORDER[b.linkRole ?? "entry"]);
144
+ const entry = sorted.some((item) => item.linkRole === "entry");
145
+ for (const item of sorted) rows.push({ order: item, child: entry && item.linkRole !== "entry" });
146
+ }
147
+ return rows;
148
+ }
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../../../utils/log-failure";
1
2
  import { useEffect, useRef, useSyncExternalStore } from "react";
2
3
  import type { BrokerAdapter } from "../../../types/broker";
3
4
  import type { BrokerInstanceConfig } from "../../../types/config";
@@ -5,6 +6,7 @@ import type { AppNotificationRequest } from "../../../types/plugin";
5
6
  import type { BrokerOrder } from "../../../types/trading";
6
7
  import { brokerOrderDone, brokerOrderStatus } from "./orders-data";
7
8
  import { orderInstrumentLabel } from "./strategy";
9
+ import { linkRoleName, trailingText } from "./linked-orders";
8
10
 
9
11
  /** Reconcile with open orders this often while the broker pushes changes. */
10
12
  const LIVE_RECONCILE_MS = 60_000;
@@ -19,6 +21,9 @@ const DONE_RETENTION_MS = 10 * 60_000;
19
21
  /** An order first seen already ended is news only if it ended this recently. */
20
22
  const RECENT_END_MS = 2 * 60_000;
21
23
  const SEEN_LIMIT = 1_000;
24
+ /** A fill re-syncs its profile's positions this long after the last fill in a burst, and no later than the cap after the first. */
25
+ const FILL_SYNC_MS = 2_000;
26
+ const FILL_SYNC_MAX_WAIT_MS = 10_000;
22
27
 
23
28
  export interface BrokerOrderFeedState {
24
29
  /** Working orders, and those that ended while watched, newest first. */
@@ -68,8 +73,11 @@ export function orderToastBody(order: BrokerOrder): string {
68
73
  const symbol = orderInstrumentLabel(order);
69
74
  const qty = (value: number) => value.toLocaleString("en-US", { maximumFractionDigits: 4 });
70
75
  const filled = order.filled > 0 && Number.isFinite(order.avgFillPrice) ? ` · ${qty(order.filled)} filled at ${order.avgFillPrice!.toLocaleString("en-US", { maximumFractionDigits: 4 })}` : "";
71
- const limit = order.orderType === "MKT" ? "market" : `${order.orderType === "STP" ? "stop" : "limit"} ${(order.limitPrice ?? order.stopPrice)?.toLocaleString("en-US", { maximumFractionDigits: 4 }) ?? ""}`.trim();
72
- return `${order.action === "BUY" ? "Buy" : "Sell"} ${qty(order.quantity)} ${symbol} ${limit}${filled}`;
76
+ const limit = order.orderType === "MKT" ? "market" : order.orderType === "TRAIL" ? trailingText(order, qty).toLowerCase()
77
+ : `${order.orderType === "STP" ? "stop" : "limit"} ${(order.limitPrice ?? order.stopPrice)?.toLocaleString("en-US", { maximumFractionDigits: 4 }) ?? ""}`.trim();
78
+ // A bracket's exit says which one filled or ended.
79
+ const role = order.linkRole && order.linkRole !== "entry" ? ` ${linkRoleName[order.linkRole].toLowerCase()}` : "";
80
+ return `${order.action === "BUY" ? "Buy" : "Sell"} ${qty(order.quantity)} ${symbol}${role} ${limit}${filled}`;
73
81
  }
74
82
 
75
83
  /**
@@ -145,7 +153,10 @@ class ProfileOrderFeed {
145
153
 
146
154
  private observe(): void {
147
155
  let state: string | undefined;
148
- try { state = this.broker.getStatus?.(this.instance).state; } catch { state = undefined; }
156
+ try { state = this.broker.getStatus?.(this.instance).state; } catch (error) {
157
+ logFailure("broker-trading.order-feed", "Read broker connection status failed")(error);
158
+ state = undefined;
159
+ }
149
160
  const connected = state === "connected";
150
161
  if (connected === this.connected) return;
151
162
  this.connected = connected;
@@ -164,7 +175,10 @@ class ProfileOrderFeed {
164
175
  this.publish();
165
176
  },
166
177
  });
167
- } catch { this.stopStream = undefined; }
178
+ } catch (error) {
179
+ logFailure("broker-trading.order-feed", "Subscribe to broker orders failed")(error);
180
+ this.stopStream = undefined;
181
+ }
168
182
  }
169
183
  void this.read();
170
184
  }
@@ -189,7 +203,10 @@ class ProfileOrderFeed {
189
203
  }
190
204
 
191
205
  private tradingOn(): boolean {
192
- try { return this.broker.getTradingCapabilities?.(this.instance)?.enabled === true; } catch { return false; }
206
+ try { return this.broker.getTradingCapabilities?.(this.instance)?.enabled === true; } catch (error) {
207
+ logFailure("broker-trading.order-feed", "Read broker trading capabilities failed")(error);
208
+ return false;
209
+ }
193
210
  }
194
211
 
195
212
  private async read(): Promise<void> {
@@ -211,7 +228,7 @@ class ProfileOrderFeed {
211
228
  for (const [key, order] of missing) {
212
229
  // A broker with no status read says nothing beyond its open list.
213
230
  if (!this.broker.getOrderStatus) { this.orders.delete(key); continue; }
214
- const status = await this.broker.getOrderStatus(this.instance, order.orderId).catch(() => undefined);
231
+ const status = await this.broker.getOrderStatus(this.instance, order.orderId).catch(logFailure("broker-trading.order-feed", "Read missing broker order status failed"));
215
232
  if (generation !== this.generation) return;
216
233
  if (status) this.apply(status, baseline ? "baseline" : "poll", false);
217
234
  // Null is the broker unable to confirm it, not an end: keep it, unconfirmed, at its last report's time,
@@ -244,6 +261,9 @@ class ProfileOrderFeed {
244
261
  if (publish) { this.prune(); this.publish(); }
245
262
  // An order that started working needs the faster cadence without a stream.
246
263
  if (!this.live && !brokerOrderDone(order.status) && (!previous || brokerOrderDone(previous.status))) this.schedule();
264
+ // One of a bracket or OCO pair ending moves the others (cancelled, or working once an entry fills): the broker's
265
+ // list says how, now rather than at the next read.
266
+ if (order.linkGroup && source !== "baseline" && source !== "poll" && brokerOrderDone(order.status) && !(previous && brokerOrderDone(previous.status))) this.refresh();
247
267
  }
248
268
 
249
269
  private prune(): void {
@@ -271,11 +291,23 @@ export class BrokerOrderFeed {
271
291
  private readonly listeners = new Set<() => void>();
272
292
  private readonly seen = new Map<string, Seen>();
273
293
  private readonly fills = new Map<string, number>();
294
+ private readonly fillSyncs = new Map<string, { timer?: ReturnType<typeof setTimeout>; firstAt: number; running: boolean; again: boolean }>();
274
295
  private notifier?: (notification: AppNotificationRequest) => void;
296
+ private syncPositions?: (instanceId: string) => Promise<unknown>;
275
297
 
276
298
  /** Toasts go through the app's notifications once the plugin is set up. */
277
299
  setNotifier(notify: ((notification: AppNotificationRequest) => void) | undefined): void { this.notifier = notify; }
278
300
 
301
+ /**
302
+ * How a fill reaches the portfolio: the app's sync of the profile's
303
+ * positions, which the plugin sets up. A fill or partial fill schedules it
304
+ * once for a burst of fills; one during a sync runs it again after.
305
+ */
306
+ setPositionSync(sync: ((instanceId: string) => Promise<unknown>) | undefined): void {
307
+ this.syncPositions = sync;
308
+ if (!sync) this.cancelFillSyncs();
309
+ }
310
+
279
311
  hold(broker: BrokerAdapter, instance: BrokerInstanceConfig): () => void {
280
312
  let profile = this.profiles.get(instance.id);
281
313
  if (!profile || profile.broker !== broker) {
@@ -298,6 +330,7 @@ export class BrokerOrderFeed {
298
330
  this.profiles.clear();
299
331
  this.seen.clear();
300
332
  this.fills.clear();
333
+ this.cancelFillSyncs();
301
334
  this.changed();
302
335
  }
303
336
 
@@ -328,11 +361,46 @@ export class BrokerOrderFeed {
328
361
  this.seen.delete(key);
329
362
  this.seen.set(key, { filled, done: (before?.done ?? false) || brokerOrderDone(order.status) });
330
363
  if (this.seen.size > SEEN_LIMIT) this.seen.delete(this.seen.keys().next().value!);
331
- if (filled > (before?.filled ?? 0)) this.fills.set(instanceId, (this.fills.get(instanceId) ?? 0) + 1);
364
+ if (filled > (before?.filled ?? 0)) {
365
+ this.fills.set(instanceId, (this.fills.get(instanceId) ?? 0) + 1);
366
+ // A fill moves the account's positions. An order first seen already filled long ago (the first read at
367
+ // launch, which the startup import covers) does not.
368
+ if (before || Date.now() - order.updatedAt <= RECENT_END_MS) this.scheduleFillSync(instanceId);
369
+ }
332
370
  if (quiet || !this.notifier) return;
333
371
  const toast = orderChangeToast(before, order, Date.now());
334
372
  if (toast) this.notifier({ ...toast, desktop: "when-inactive", body: orderToastBody(order) });
335
373
  }
374
+
375
+ private scheduleFillSync(instanceId: string): void {
376
+ if (!this.syncPositions) return;
377
+ const now = Date.now();
378
+ const entry = this.fillSyncs.get(instanceId) ?? { firstAt: now, running: false, again: false };
379
+ if (entry.timer === undefined) entry.firstAt = now;
380
+ else clearTimeout(entry.timer);
381
+ entry.timer = setTimeout(() => this.runFillSync(instanceId), Math.max(0, Math.min(FILL_SYNC_MS, entry.firstAt + FILL_SYNC_MAX_WAIT_MS - now)));
382
+ this.fillSyncs.set(instanceId, entry);
383
+ }
384
+
385
+ private runFillSync(instanceId: string): void {
386
+ const entry = this.fillSyncs.get(instanceId);
387
+ const sync = this.syncPositions;
388
+ if (!entry || !sync) return;
389
+ entry.timer = undefined;
390
+ if (entry.running) { entry.again = true; return; }
391
+ entry.running = true;
392
+ // A failed sync leaves the portfolio as it was; the next fill, a manual sync or the next launch reads it again.
393
+ void Promise.resolve().then(() => sync(instanceId)).catch(logFailure("broker-trading.order-feed", "Sync positions after broker fill failed")).finally(() => {
394
+ entry.running = false;
395
+ if (this.fillSyncs.get(instanceId) !== entry) return;
396
+ if (entry.again) { entry.again = false; this.runFillSync(instanceId); } else if (entry.timer === undefined) this.fillSyncs.delete(instanceId);
397
+ });
398
+ }
399
+
400
+ private cancelFillSyncs(): void {
401
+ for (const entry of this.fillSyncs.values()) if (entry.timer !== undefined) clearTimeout(entry.timer);
402
+ this.fillSyncs.clear();
403
+ }
336
404
  }
337
405
 
338
406
  export const brokerOrderFeed = new BrokerOrderFeed();