gloomberb 0.18.0 → 0.18.2

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 (365) hide show
  1. package/package.json +1 -1
  2. package/src/api-client/commodity-balances.ts +6 -0
  3. package/src/api-client/company-kpis.ts +9 -0
  4. package/src/api-client/crypto-markets.ts +9 -0
  5. package/src/api-client/data.ts +6 -5
  6. package/src/api-client/equity-screener.ts +2 -0
  7. package/src/api-client/market-movers.ts +7 -0
  8. package/src/api-client/types.ts +14 -0
  9. package/src/app/pane-runtime/plugin-bindings.ts +3 -1
  10. package/src/app/pane-runtime/ticker-open-runtime.ts +17 -5
  11. package/src/app/runtime/plugin-bindings.ts +6 -2
  12. package/src/brokers/logos.ts +66 -0
  13. package/src/brokers/profile-form.ts +3 -0
  14. package/src/brokers/remote-broker-adapter.ts +13 -3
  15. package/src/capabilities/index.ts +1 -1
  16. package/src/capabilities/service-capabilities.ts +7 -1
  17. package/src/cli/base-converter.ts +60 -18
  18. package/src/cli/commands/fred-series.ts +15 -5
  19. package/src/cli/commands/market.ts +290 -159
  20. package/src/cli/commands/news-search.ts +35 -8
  21. package/src/cli/commands/overview.ts +31 -6
  22. package/src/cli/commands/system.ts +35 -1
  23. package/src/cli/commands/ticker.ts +105 -52
  24. package/src/cli/currency-hints.ts +27 -0
  25. package/src/cli/fx-pair.ts +41 -4
  26. package/src/cli/glossary.ts +57 -4
  27. package/src/cli/helpers.ts +36 -3
  28. package/src/cli/index.ts +4 -1
  29. package/src/cli/known-gaps.ts +37 -1
  30. package/src/cli/listing-arg.ts +37 -2
  31. package/src/cli/pane-functions/catalog.ts +3 -1
  32. package/src/cli/pane-functions/discovery.ts +15 -1
  33. package/src/cli/pane-functions/dom.ts +9 -4
  34. package/src/cli/pane-functions/freshness.ts +4 -4
  35. package/src/cli/pane-functions/function-help.ts +31 -7
  36. package/src/cli/pane-functions/headless.ts +6 -5
  37. package/src/cli/pane-functions/index.ts +3 -2
  38. package/src/cli/pane-functions/lock-note.ts +7 -4
  39. package/src/cli/pane-functions/resolver.ts +2 -1
  40. package/src/cli/quote-answer.ts +179 -0
  41. package/src/cli/series-search.ts +2 -1
  42. package/src/cli/symbol-list.ts +51 -16
  43. package/src/cli/types.ts +4 -0
  44. package/src/components/chart/composite/composite-chart.tsx +22 -7
  45. package/src/components/chart/composite/format.ts +13 -1
  46. package/src/components/chart/composite/level-labels.ts +30 -0
  47. package/src/components/chart/composite/levels.ts +17 -6
  48. package/src/components/chart/composite/panel-surface.tsx +64 -16
  49. package/src/components/chart/composite/price-axis-labels.tsx +2 -1
  50. package/src/components/chart/composite/types.ts +4 -0
  51. package/src/components/command-bar/routes/ticker-search/actions.ts +6 -0
  52. package/src/components/command-bar/workflow/collection-actions.ts +4 -0
  53. package/src/components/index.ts +2 -0
  54. package/src/components/layout/pane/content.tsx +16 -14
  55. package/src/components/layout/pane/footer/index.tsx +22 -3
  56. package/src/components/layout/pane/footer/model.ts +99 -4
  57. package/src/components/layout/pane/header-tabs.tsx +15 -1
  58. package/src/components/onboarding/onboarding-frame.tsx +9 -11
  59. package/src/components/onboarding/use-onboarding-broker-form.ts +5 -2
  60. package/src/components/onboarding/wizard-model.ts +10 -3
  61. package/src/components/onboarding/wizard-positions.ts +8 -0
  62. package/src/components/ui/button.tsx +11 -4
  63. package/src/components/ui/choice-dialog.tsx +8 -5
  64. package/src/components/ui/display.tsx +11 -6
  65. package/src/components/ui/fields.tsx +100 -20
  66. package/src/components/ui/icon.tsx +6 -1
  67. package/src/components/ui/list-row-lines.tsx +87 -0
  68. package/src/components/ui/list-view.tsx +14 -0
  69. package/src/components/ui/logo-mark.tsx +64 -0
  70. package/src/components/ui/number-step.ts +54 -0
  71. package/src/components/ui/select-button.tsx +5 -4
  72. package/src/components/ui/select-field.tsx +3 -3
  73. package/src/components/ui/status.tsx +17 -3
  74. package/src/components/ui/tabs.tsx +9 -1
  75. package/src/components/ui/toggle.tsx +17 -3
  76. package/src/core/state/app/instrument.ts +8 -3
  77. package/src/data/config/store/normalize.ts +4 -1
  78. package/src/data/plugin-cache.ts +32 -6
  79. package/src/docs/snapshot.json +57 -22
  80. package/src/i18n/es.ts +9 -3
  81. package/src/i18n/ja.ts +9 -3
  82. package/src/i18n/ko.ts +9 -3
  83. package/src/i18n/zh-cn.ts +10 -4
  84. package/src/i18n/zh-tw.ts +10 -4
  85. package/src/market-data/commodity-names.ts +80 -0
  86. package/src/market-data/coordinator/auxiliary.ts +3 -1
  87. package/src/market-data/coordinator/chart.ts +3 -1
  88. package/src/market-data/futures-board.ts +38 -1
  89. package/src/market-data/futures-contract.ts +91 -0
  90. package/src/market-data/fx-closes.ts +78 -1
  91. package/src/market-data/market/change-basis.ts +219 -0
  92. package/src/market-data/market/format.ts +28 -12
  93. package/src/market-data/market/freshness.ts +38 -0
  94. package/src/market-data/market/status.ts +55 -9
  95. package/src/market-data/market/year-to-date.ts +46 -0
  96. package/src/market-data/quotes/contributions.ts +12 -0
  97. package/src/market-data/quotes/delisting.ts +34 -0
  98. package/src/market-data/quotes/live-valuation.ts +23 -0
  99. package/src/market-data/quotes/resolution.ts +8 -2
  100. package/src/market-data/tax-equivalent.ts +10 -2
  101. package/src/notifications/app-notifier.ts +2 -2
  102. package/src/notifications/notification-log.ts +8 -6
  103. package/src/plugins/builtin/alerts/cli.ts +26 -18
  104. package/src/plugins/builtin/alerts/headless.ts +1 -1
  105. package/src/plugins/builtin/alerts/index.tsx +13 -4
  106. package/src/plugins/builtin/analytics/index.tsx +1 -1
  107. package/src/plugins/builtin/analytics/risk-client.ts +44 -11
  108. package/src/plugins/builtin/analytics/risk-fx.ts +61 -0
  109. package/src/plugins/builtin/analytics/risk-headless.ts +10 -1
  110. package/src/plugins/builtin/analytics/risk-model.ts +79 -16
  111. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  112. package/src/plugins/builtin/analytics/risk-test-data.ts +3 -0
  113. package/src/plugins/builtin/attention/trending.tsx +8 -6
  114. package/src/plugins/builtin/backtest/client.ts +5 -2
  115. package/src/plugins/builtin/backtest/window.ts +16 -3
  116. package/src/plugins/builtin/broker-manager/add-flow.tsx +40 -8
  117. package/src/plugins/builtin/broker-manager/detail.tsx +26 -1
  118. package/src/plugins/builtin/broker-manager/index.tsx +20 -9
  119. package/src/plugins/builtin/broker-manager/model.ts +5 -0
  120. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -1
  121. package/src/plugins/builtin/broker-manager/table.ts +5 -1
  122. package/src/plugins/builtin/broker-trading/account-picker.tsx +91 -0
  123. package/src/plugins/builtin/broker-trading/chart-levels.ts +54 -0
  124. package/src/plugins/builtin/broker-trading/command.ts +103 -0
  125. package/src/plugins/builtin/broker-trading/controller.ts +688 -0
  126. package/src/plugins/builtin/broker-trading/dock.tsx +182 -0
  127. package/src/plugins/builtin/broker-trading/headless.ts +76 -0
  128. package/src/plugins/builtin/broker-trading/index.tsx +77 -0
  129. package/src/plugins/builtin/broker-trading/order-feed.ts +383 -0
  130. package/src/plugins/builtin/broker-trading/orders-data.ts +133 -0
  131. package/src/plugins/builtin/broker-trading/orders.tsx +437 -0
  132. package/src/plugins/builtin/broker-trading/overview-orders.tsx +121 -0
  133. package/src/plugins/builtin/broker-trading/pane-model.ts +207 -0
  134. package/src/plugins/builtin/broker-trading/pane.tsx +505 -0
  135. package/src/plugins/builtin/broker-trading/strategy.ts +153 -0
  136. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +117 -0
  137. package/src/plugins/builtin/broker-trading/ticket.tsx +573 -0
  138. package/src/plugins/builtin/buildout/data-runtime.ts +1 -1
  139. package/src/plugins/builtin/buildout/format.ts +1 -0
  140. package/src/plugins/builtin/buildout/model/index.ts +16 -7
  141. package/src/plugins/builtin/buildout/pane/index.tsx +3 -2
  142. package/src/plugins/builtin/buildout/pane/status.tsx +3 -3
  143. package/src/plugins/builtin/chart-composer/cli-options.ts +19 -0
  144. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  145. package/src/plugins/builtin/chart-composer/headless-schema.ts +3 -3
  146. package/src/plugins/builtin/chart-composer/headless.ts +2 -1
  147. package/src/plugins/builtin/chart-composer/option-range.ts +39 -0
  148. package/src/plugins/builtin/chart-composer/pane.tsx +31 -11
  149. package/src/plugins/builtin/chart-composer/presets.ts +2 -2
  150. package/src/plugins/builtin/chart-composer/series-catalog.ts +4 -2
  151. package/src/plugins/builtin/chart-composer/shortcuts.ts +3 -3
  152. package/src/plugins/builtin/chart-composer/viewport-labels.ts +5 -2
  153. package/src/plugins/builtin/chat/index.tsx +1 -1
  154. package/src/plugins/builtin/chat/status-widget.tsx +1 -1
  155. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +6 -1
  156. package/src/plugins/builtin/chat/unread-inbox.ts +1 -1
  157. package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -3
  158. package/src/plugins/builtin/cloud/team/pane.tsx +30 -9
  159. package/src/plugins/builtin/commodity-balances/client.ts +6 -4
  160. package/src/plugins/builtin/commodity-balances/headless.ts +41 -7
  161. package/src/plugins/builtin/commodity-balances/index.tsx +19 -4
  162. package/src/plugins/builtin/commodity-balances/model.ts +22 -0
  163. package/src/plugins/builtin/commodity-balances/pane.tsx +36 -5
  164. package/src/plugins/builtin/company-kpis/client.ts +3 -2
  165. package/src/plugins/builtin/company-kpis/headless.ts +5 -7
  166. package/src/plugins/builtin/company-kpis/model.ts +85 -2
  167. package/src/plugins/builtin/company-kpis/pane.tsx +26 -12
  168. package/src/plugins/builtin/composite-plugins.ts +3 -2
  169. package/src/plugins/builtin/cot/headless.ts +1 -1
  170. package/src/plugins/builtin/cot/index.tsx +1 -1
  171. package/src/plugins/builtin/cot/model.ts +35 -12
  172. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  173. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +2 -1
  174. package/src/plugins/builtin/credit-conditions/index.tsx +2 -1
  175. package/src/plugins/builtin/crypto-board/client.ts +33 -2
  176. package/src/plugins/builtin/crypto-board/headless.ts +5 -2
  177. package/src/plugins/builtin/crypto-board/model.ts +28 -8
  178. package/src/plugins/builtin/custom-view/loader.ts +3 -2
  179. package/src/plugins/builtin/custom-view/pane.tsx +5 -4
  180. package/src/plugins/builtin/custom-view/view-spec.ts +1 -0
  181. package/src/plugins/builtin/dividend-yield/headless.ts +30 -8
  182. package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
  183. package/src/plugins/builtin/earnings-calls/headless.ts +4 -4
  184. package/src/plugins/builtin/earnings-calls/lock-note.ts +12 -3
  185. package/src/plugins/builtin/econ/calendar-model.ts +38 -2
  186. package/src/plugins/builtin/econ/fred-catalog.ts +6 -2
  187. package/src/plugins/builtin/econ/fred-series-map.ts +12 -1
  188. package/src/plugins/builtin/econ/index.tsx +31 -4
  189. package/src/plugins/builtin/econ/print-watch.ts +279 -0
  190. package/src/plugins/builtin/equity-screener/headless.ts +20 -5
  191. package/src/plugins/builtin/equity-screener/model.ts +40 -2
  192. package/src/plugins/builtin/equity-screener/pane.tsx +24 -21
  193. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -2
  194. package/src/plugins/builtin/estimate-revisions/lock-note.ts +29 -7
  195. package/src/plugins/builtin/executives/data.ts +24 -8
  196. package/src/plugins/builtin/executives/index.tsx +2 -18
  197. package/src/plugins/builtin/executives/pane.tsx +34 -10
  198. package/src/plugins/builtin/exposure/client.ts +4 -1
  199. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  200. package/src/plugins/builtin/exposure/evidence.ts +4 -1
  201. package/src/plugins/builtin/exposure/holdings.ts +3 -1
  202. package/src/plugins/builtin/exposure/pane.tsx +37 -16
  203. package/src/plugins/builtin/filing-events/index.tsx +9 -3
  204. package/src/plugins/builtin/futures/table.tsx +6 -3
  205. package/src/plugins/builtin/futures-curve/pane.tsx +2 -2
  206. package/src/plugins/builtin/insider/base-values.ts +72 -0
  207. package/src/plugins/builtin/insider/headless.ts +29 -4
  208. package/src/plugins/builtin/jobs/headless.ts +2 -2
  209. package/src/plugins/builtin/jobs/lock-note.ts +14 -3
  210. package/src/plugins/builtin/market-halts/model.ts +31 -6
  211. package/src/plugins/builtin/market-halts/pane.tsx +3 -0
  212. package/src/plugins/builtin/market-movers/client.ts +2 -2
  213. package/src/plugins/builtin/market-movers/index.tsx +0 -3
  214. package/src/plugins/builtin/market-movers/session-body.tsx +27 -11
  215. package/src/plugins/builtin/market-movers/session.ts +25 -10
  216. package/src/plugins/builtin/market-valuation/pane.tsx +2 -1
  217. package/src/plugins/builtin/members/pane.tsx +8 -3
  218. package/src/plugins/builtin/news/wire/feed-config.ts +33 -13
  219. package/src/plugins/builtin/news/wire/index.tsx +7 -2
  220. package/src/plugins/builtin/news/wire/letters/client.ts +1 -0
  221. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +6 -3
  222. package/src/plugins/builtin/news/wire/news/detail-view.tsx +2 -1
  223. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +11 -3
  224. package/src/plugins/builtin/news/wire/news/table.tsx +2 -1
  225. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -2
  226. package/src/plugins/builtin/news/wire/rss/feed-xml.ts +6 -1
  227. package/src/plugins/builtin/news/wire/rss/managed-feed.ts +1 -0
  228. package/src/plugins/builtin/news/wire/rss/source.ts +13 -4
  229. package/src/plugins/builtin/notes/cli.ts +33 -8
  230. package/src/plugins/builtin/notes/export.ts +49 -3
  231. package/src/plugins/builtin/notes/files.ts +14 -3
  232. package/src/plugins/builtin/notes/index.tsx +3 -3
  233. package/src/plugins/builtin/notes/quick-notes-pane.tsx +45 -10
  234. package/src/plugins/builtin/notes/store.ts +13 -3
  235. package/src/plugins/builtin/options/footer.ts +25 -20
  236. package/src/plugins/builtin/options/live-quotes.ts +33 -18
  237. package/src/plugins/builtin/options/view.tsx +77 -22
  238. package/src/plugins/builtin/options-calculator/pane.tsx +4 -2
  239. package/src/plugins/builtin/options-scenario/pane.tsx +41 -6
  240. package/src/plugins/builtin/portfolio-list/account-metrics.ts +3 -0
  241. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +8 -7
  242. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +166 -15
  243. package/src/plugins/builtin/portfolio-list/cli/render.ts +69 -15
  244. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +2 -2
  245. package/src/plugins/builtin/portfolio-list/headless.ts +3 -1
  246. package/src/plugins/builtin/portfolio-list/pane/data.ts +23 -1
  247. package/src/plugins/builtin/portfolio-list/pane/index.tsx +44 -17
  248. package/src/plugins/builtin/portfolio-list/settings.ts +20 -2
  249. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -1
  250. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +42 -7
  251. package/src/plugins/builtin/portfolio-list/summary/totals.ts +24 -3
  252. package/src/plugins/builtin/primary-dealers/client.ts +137 -62
  253. package/src/plugins/builtin/primary-dealers/headless.ts +30 -5
  254. package/src/plugins/builtin/primary-dealers/index.tsx +3 -2
  255. package/src/plugins/builtin/primary-dealers/model.ts +179 -129
  256. package/src/plugins/builtin/primary-dealers/pane.tsx +135 -15
  257. package/src/plugins/builtin/research/analyst-model.ts +5 -1
  258. package/src/plugins/builtin/risk-factors/index.tsx +12 -3
  259. package/src/plugins/builtin/scanner/feed.ts +6 -1
  260. package/src/plugins/builtin/scanner/headless.ts +1 -1
  261. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  262. package/src/plugins/builtin/series-inventory.ts +1 -0
  263. package/src/plugins/builtin/shared/broker-reconnect.ts +105 -0
  264. package/src/plugins/builtin/shared/cloud-denied.ts +7 -6
  265. package/src/plugins/builtin/shared/cloud-upgrade.ts +10 -11
  266. package/src/plugins/builtin/shared/free-routes.ts +24 -0
  267. package/src/plugins/builtin/shared/headless-options.ts +11 -0
  268. package/src/plugins/builtin/shared/pro-feature-wall.tsx +9 -4
  269. package/src/plugins/builtin/shared/pro-lock-note.ts +89 -7
  270. package/src/plugins/builtin/shared/research-trading.tsx +62 -0
  271. package/src/plugins/builtin/social-mentions/headless.ts +1 -1
  272. package/src/plugins/builtin/social-mentions/lock-note.ts +1 -1
  273. package/src/plugins/builtin/themes/headless.ts +8 -2
  274. package/src/plugins/builtin/themes/model.ts +43 -10
  275. package/src/plugins/builtin/themes/pane.tsx +8 -6
  276. package/src/plugins/builtin/thirteenf/api.ts +4 -1
  277. package/src/plugins/builtin/thirteenf/data.ts +5 -1
  278. package/src/plugins/builtin/thirteenf/headless.ts +3 -1
  279. package/src/plugins/builtin/thirteenf/pane.tsx +8 -3
  280. package/src/plugins/builtin/ticker-detail/overview/model.ts +44 -10
  281. package/src/plugins/builtin/ticker-detail/overview/option-contract.ts +64 -0
  282. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +82 -21
  283. package/src/plugins/builtin/ticker-detail/pane.tsx +21 -14
  284. package/src/plugins/builtin/ticker-detail/quote-footer.ts +5 -1
  285. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +37 -8
  286. package/src/plugins/builtin/time-sales/pane.tsx +2 -2
  287. package/src/plugins/builtin/trace-bonds/client.ts +14 -3
  288. package/src/plugins/builtin/trace-bonds/headless.ts +1 -1
  289. package/src/plugins/builtin/vol-surface/pane.tsx +2 -2
  290. package/src/plugins/builtin-editorial.ts +1 -1
  291. package/src/plugins/compat.ts +3 -0
  292. package/src/plugins/external-runtime.ts +5 -0
  293. package/src/plugins/loader.ts +6 -0
  294. package/src/plugins/plugin-icon.ts +61 -0
  295. package/src/plugins/registry/host-actions.ts +2 -1
  296. package/src/plugins/registry/index.ts +9 -1
  297. package/src/plugins/runtime/context.tsx +5 -0
  298. package/src/plugins/runtime/index.tsx +3 -0
  299. package/src/portfolio/column-values.ts +13 -4
  300. package/src/portfolio/cost-fx.ts +61 -0
  301. package/src/portfolio/mutations.ts +16 -2
  302. package/src/portfolio/position-metrics.ts +45 -4
  303. package/src/portfolio/position-quantity.ts +43 -0
  304. package/src/public/__snapshots__/public-api.test.ts.snap +5 -0
  305. package/src/public/broker.ts +8 -0
  306. package/src/public/market-data.ts +5 -0
  307. package/src/renderers/dom/desktop/control-styles.ts +7 -1
  308. package/src/renderers/dom/desktop/controls.tsx +39 -7
  309. package/src/renderers/dom/desktop/icons.tsx +14 -0
  310. package/src/renderers/dom/desktop/list-view.tsx +51 -19
  311. package/src/renderers/dom/desktop/menu.tsx +10 -1
  312. package/src/renderers/dom/desktop/select-field.tsx +8 -6
  313. package/src/renderers/dom/host/style.ts +6 -1
  314. package/src/renderers/dom/host/tabs.tsx +6 -0
  315. package/src/renderers/dom/styles.css +12 -5
  316. package/src/renderers/electrobun/bun/core-capabilities.ts +12 -0
  317. package/src/renderers/electrobun/bun/external-plugins.ts +3 -0
  318. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  319. package/src/renderers/electrobun/view/broker-remote-client.ts +16 -0
  320. package/src/renderers/electrobun/view/external-plugins.ts +1 -0
  321. package/src/series/validate.ts +2 -2
  322. package/src/sources/gloomberb-cloud/fred-series.ts +27 -2
  323. package/src/sources/gloomberb-cloud/index.ts +50 -17
  324. package/src/sources/gloomberb-cloud/normalizers.ts +4 -0
  325. package/src/sources/history-result.ts +3 -0
  326. package/src/sources/provider-errors.ts +3 -1
  327. package/src/sources/provider-router/history.ts +49 -6
  328. package/src/sources/provider-router/index.ts +2 -1
  329. package/src/state/app/activity.ts +13 -0
  330. package/src/sync/controller.ts +4 -0
  331. package/src/sync/core-contributors.ts +63 -11
  332. package/src/sync/local-only.ts +121 -0
  333. package/src/sync/react.ts +6 -0
  334. package/src/sync/types.ts +3 -0
  335. package/src/team/collections.ts +2 -0
  336. package/src/tickers/listing-fate.ts +27 -8
  337. package/src/tickers/metadata.ts +10 -0
  338. package/src/tickers/search/index.ts +24 -3
  339. package/src/time-series/price-comparison.ts +35 -0
  340. package/src/time-series/resolve.ts +52 -11
  341. package/src/time-series/spec.ts +1 -1
  342. package/src/time-series/types.ts +5 -0
  343. package/src/types/broker.ts +56 -1
  344. package/src/types/config.ts +6 -0
  345. package/src/types/data-provider.ts +5 -1
  346. package/src/types/financials.ts +23 -0
  347. package/src/types/headless.ts +12 -2
  348. package/src/types/instrument.ts +4 -0
  349. package/src/types/plugin.ts +24 -6
  350. package/src/types/price-history.ts +6 -1
  351. package/src/types/ticker.ts +9 -0
  352. package/src/types/trading.ts +67 -0
  353. package/src/ui/host.tsx +4 -0
  354. package/src/utils/base-currency.ts +108 -0
  355. package/src/utils/currency-names.ts +128 -0
  356. package/src/utils/currency-pair.ts +2 -1
  357. package/src/utils/currency-units.ts +41 -1
  358. package/src/utils/dom-screenshot.ts +22 -9
  359. package/src/utils/exchange-notation.generated.ts +360 -0
  360. package/src/utils/exchanges.ts +15 -322
  361. package/src/utils/format.ts +31 -1
  362. package/src/utils/instrument-identity.ts +38 -1
  363. package/src/utils/options.ts +46 -1
  364. package/src/utils/price-history.ts +4 -0
  365. package/src/utils/reported-money.ts +8 -4
@@ -0,0 +1,207 @@
1
+ import { canonicalExchange, parsePublicTickerKey } from "../../../utils/exchanges";
2
+ import { parseOptionTicker } from "../../../utils/options";
3
+ import type { PaneTickerIdentity } from "../../../state/hooks/pane-ticker";
4
+ import type { BrokerAdapter, BrokerPosition } from "../../../types/broker";
5
+ import type { BrokerInstanceConfig } from "../../../types/config";
6
+ import type { BrokerContractRef } from "../../../types/instrument";
7
+ import type { Quote } from "../../../types/financials";
8
+ import type { BrokerAccount, BrokerOrderRequest, BrokerTradingCapabilities } from "../../../types/trading";
9
+ import type { BrokerAccountChoice } from "./account-picker";
10
+ import { reverseStrategy, strategyContract, strategyOrderLegs, type StrategyIntent } from "./strategy";
11
+
12
+ /** Successful broker reads may publish status heartbeats. They must not schedule another read. */
13
+ export function subscribeTradingStatus(broker: BrokerAdapter, profile: BrokerInstanceConfig, onChange: () => void): () => void {
14
+ const key = () => {
15
+ const status = broker.getStatus?.(profile);
16
+ return JSON.stringify([status?.state, status?.mode, status?.quoteData]);
17
+ };
18
+ let previous = key();
19
+ return broker.subscribeStatus?.(profile, () => {
20
+ const next = key();
21
+ if (next === previous) return;
22
+ previous = next;
23
+ onChange();
24
+ }) ?? (() => {});
25
+ }
26
+
27
+ export interface ProfileAccountChoice extends BrokerAccountChoice { profileId: string; accountId: string }
28
+
29
+ export function ticketAccountChoiceKey(profileId: string, accountId: string): string { return JSON.stringify([profileId, accountId]); }
30
+
31
+ export function buildTicketAccountChoices(profiles: { profile: BrokerInstanceConfig; accounts: BrokerAccount[] }[]): ProfileAccountChoice[] {
32
+ return profiles.flatMap(({ profile, accounts }) => accounts.map((account, index) => ({
33
+ value: ticketAccountChoiceKey(profile.id, account.accountId), label: account.name || `Account ${index + 1}`, profileLabel: profile.label,
34
+ tradingMode: account.tradingMode, profileId: profile.id, accountId: account.accountId,
35
+ })));
36
+ }
37
+
38
+ export interface TicketPositionSnapshot {
39
+ profile: BrokerInstanceConfig;
40
+ positions: BrokerPosition[];
41
+ fetchedAt: number;
42
+ }
43
+
44
+ /** A missing or expired snapshot is unknown, while a successful empty snapshot means zero. */
45
+ export function ticketPositionContext(
46
+ snapshot: TicketPositionSnapshot | undefined, profile: BrokerInstanceConfig, accountId: string | undefined, contract: BrokerContractRef, now = Date.now(),
47
+ ): { quantity: number; avgCost?: number; pnl?: number } | undefined {
48
+ if (!snapshot || !accountId || snapshot.profile.id !== profile.id || snapshot.profile.config !== profile.config || now - snapshot.fetchedAt > 60_000 || !contract.symbol) return undefined;
49
+ const normalize = (symbol: string) => symbol.replace(/^O:/, "").replace(/\s+/g, "").toUpperCase();
50
+ const stock = (type: string | undefined) => !type || type === "STK" || type === "ETF";
51
+ const matched = snapshot.positions.filter((position) => {
52
+ if (position.accountId !== accountId) return false;
53
+ const held = position.brokerContract;
54
+ if (!held) return stock(contract.secType) && stock(position.assetCategory) && normalize(position.ticker) === normalize(contract.symbol);
55
+ if (held.brokerId !== contract.brokerId || (held.brokerInstanceId && held.brokerInstanceId !== profile.id)) return false;
56
+ if (held.conId !== undefined && contract.conId !== undefined) return held.conId === contract.conId;
57
+ if (!(stock(held.secType) && stock(contract.secType)) && held.secType !== contract.secType) return false;
58
+ if (held.localSymbol && contract.localSymbol && normalize(held.localSymbol) === normalize(contract.localSymbol) && (stock(contract.secType) || normalize(contract.localSymbol) !== normalize(contract.symbol))) return true;
59
+ if (normalize(held.symbol) !== normalize(contract.symbol)) return false;
60
+ if (stock(contract.secType)) return true;
61
+ return !!contract.lastTradeDateOrContractMonth && held.lastTradeDateOrContractMonth === contract.lastTradeDateOrContractMonth && held.right === contract.right && held.strike === contract.strike && held.multiplier === contract.multiplier;
62
+ });
63
+ if (matched.some((position) => !Number.isFinite(position.shares))) return undefined;
64
+ const quantity = matched.reduce((total, position) => total + (position.side === "short" ? -Math.abs(position.shares) : position.shares), 0);
65
+ const weight = matched.reduce((total, position) => total + Math.abs(position.shares), 0);
66
+ const avgCost = weight > 0 && matched.every((position) => position.avgCost !== undefined && Number.isFinite(position.avgCost)) ? matched.reduce((total, position) => total + position.avgCost! * Math.abs(position.shares), 0) / weight : undefined;
67
+ const pnl = matched.length && matched.every((position) => position.unrealizedPnl !== undefined && Number.isFinite(position.unrealizedPnl)) ? matched.reduce((total, position) => total + position.unrealizedPnl!, 0) : undefined;
68
+ return { quantity, avgCost, pnl };
69
+ }
70
+
71
+ export function availableTicketPosition(snapshot: TicketPositionSnapshot | undefined, profile: BrokerInstanceConfig, accountId: string | undefined, contract: BrokerContractRef, now = Date.now()): number | undefined {
72
+ return ticketPositionContext(snapshot, profile, accountId, contract, now)?.quantity;
73
+ }
74
+
75
+ /** Quick sizing rounds down to the broker's allowed increment and never assumes margin. */
76
+ export function quickTicketQuantity(draft: BrokerOrderRequest, capabilities: BrokerTradingCapabilities, quote: Quote | undefined, account: BrokerAccount | undefined, held: number | undefined, fraction: number): number {
77
+ let maximum: number;
78
+ if (draft.action === "SELL") {
79
+ if (held === undefined || !Number.isFinite(held) || held <= 0) throw new Error("Refresh the held position before choosing a sell quantity.");
80
+ maximum = held;
81
+ } else {
82
+ const brokerPrice = quote?.ask ?? quote?.price;
83
+ const multiplier = Number(draft.contract.multiplier ?? 1);
84
+ const price = Math.max(brokerPrice ?? NaN, draft.limitPrice ?? 0, draft.stopPrice ?? 0);
85
+ if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(multiplier) || multiplier <= 0
86
+ || account?.buyingPower === undefined || !Number.isFinite(account.buyingPower) || account.buyingPower <= 0) {
87
+ throw new Error("Load the broker quote and buying power before choosing a buy quantity.");
88
+ }
89
+ maximum = account.buyingPower / (price * multiplier);
90
+ }
91
+ const step = capabilities.quantityStep ?? (capabilities.fractionalQuantity ? 0.000001 : 1);
92
+ if (!Number.isFinite(step) || step <= 0 || !Number.isFinite(fraction) || fraction <= 0 || fraction > 1) throw new Error("This broker cannot calculate a valid quick quantity.");
93
+ const target = maximum * fraction;
94
+ let quantity = Number((Math.floor(target / step + 1e-10) * step).toFixed(8));
95
+ if (!capabilities.fractionalQuantity && !Number.isInteger(quantity)) {
96
+ quantity = Math.floor(quantity);
97
+ for (let count = 0; count < 1000 && quantity > 0 && Math.abs(quantity / step - Math.round(quantity / step)) > 1e-8; count++) quantity--;
98
+ }
99
+ if (!Number.isFinite(quantity) || quantity <= 0 || quantity < (capabilities.minQuantity ?? 0) || quantity > maximum + 1e-8
100
+ || Math.abs(quantity / step - Math.round(quantity / step)) > 1e-8) {
101
+ throw new Error("The available amount is below the broker's minimum order quantity.");
102
+ }
103
+ return quantity;
104
+ }
105
+
106
+ const LISTING_SEC_TYPES: Record<string, string> = {
107
+ stock: "STK", equity: "STK", etf: "ETF", option: "OPT", future: "FUT", futures: "FUT", bond: "BOND", crypto: "CRYPTO", currency: "CASH", forex: "CASH",
108
+ };
109
+ /** A ticker's category as a broker security type: a saved ticker carries one ("STK", "ETF"), a listing picked in search its type ("stock"). */
110
+ export function tickerSecType(category: string | undefined, fallback = "STK"): string {
111
+ return category ? LISTING_SEC_TYPES[category.toLowerCase()] ?? category.toUpperCase() : fallback;
112
+ }
113
+
114
+ /**
115
+ * Broker contract IDs are meaningful only inside their original broker
116
+ * profile. A pane on another broker's or profile's contract (a search result
117
+ * or position from it) trades here by its public listing, as a pane on a plain
118
+ * listing does: its symbol, type, currency and venue, and an option's series.
119
+ * The other broker's IDs never carry over.
120
+ */
121
+ export function resolveTicketContract(identity: PaneTickerIdentity, broker: BrokerAdapter, instance: BrokerInstanceConfig): { contract?: BrokerContractRef; error?: string } {
122
+ if (identity.error) return { error: identity.error };
123
+ if (!identity.symbol) return { error: "Choose a ticker to trade." };
124
+ const held = identity.contract ?? undefined;
125
+ if (held && held.brokerId === broker.id && (!held.brokerInstanceId || held.brokerInstanceId === instance.id)) {
126
+ return { contract: { ...held, brokerInstanceId: instance.id } };
127
+ }
128
+ const listing = parsePublicTickerKey(identity.symbol);
129
+ const secType = held?.secType ? tickerSecType(held.secType) : tickerSecType(identity.ticker?.metadata.assetCategory);
130
+ // A combination is its legs' IDs at the broker that built it, which name nothing here.
131
+ if (secType === "BAG") return { error: `${broker.name} cannot trade ${listing.symbol}.` };
132
+ const venue = [listing.exchange, identity.ticker?.metadata.exchange, held?.primaryExchange, held?.exchange].find((value) => !!value && canonicalExchange(value) !== "SMART");
133
+ const series = secType === "OPT" && held ? Object.fromEntries(Object.entries({ lastTradeDateOrContractMonth: held.lastTradeDateOrContractMonth, right: held.right, strike: held.strike, multiplier: held.multiplier })
134
+ .filter(([, value]) => value !== undefined)) as Partial<BrokerContractRef> : {};
135
+ return { contract: { brokerId: broker.id, brokerInstanceId: instance.id, symbol: listing.symbol, localSymbol: listing.symbol,
136
+ secType, currency: identity.ticker?.metadata.currency || held?.currency || "USD", exchange: venue,
137
+ // A listed option's OCC symbol names a standard contract of 100 shares, which its costs count.
138
+ ...(secType === "OPT" && parseOptionTicker(listing.symbol) ? { multiplier: "100" } : {}), ...series } };
139
+ }
140
+
141
+ export function chooseTicketAccount(accounts: BrokerAccount[], previous?: string): string | undefined {
142
+ if (accounts.some((account) => account.accountId === previous)) return previous;
143
+ return accounts.length === 1 && accounts[0]?.tradingMode === "simulation" ? accounts[0].accountId : undefined;
144
+ }
145
+
146
+ function defaultQuantity(capabilities: BrokerTradingCapabilities | undefined): number {
147
+ const minimum = capabilities?.minQuantity;
148
+ const start = Math.max(1, minimum && Number.isFinite(minimum) ? minimum : 1);
149
+ const step = capabilities?.quantityStep;
150
+ if (!step || !Number.isFinite(step) || step <= 0) return capabilities?.fractionalQuantity ? start : Math.ceil(start);
151
+ const quantity = Number((Math.ceil(start / step - 1e-10) * step).toFixed(8));
152
+ if (capabilities?.fractionalQuantity || Number.isInteger(quantity)) return quantity;
153
+ // Some brokers combine a fractional increment with whole-quantity instruments.
154
+ // Never prefill an invalid amount if those constraints cannot be reconciled.
155
+ for (let integer = Math.ceil(start); integer <= Math.ceil(start) + 1000; integer++) {
156
+ if (Math.abs(integer / step - Math.round(integer / step)) < 1e-8) return integer;
157
+ }
158
+ return NaN;
159
+ }
160
+
161
+ export function newTicketDraft(broker: BrokerAdapter, instance: BrokerInstanceConfig, contract: BrokerContractRef, accountId?: string, action: "BUY" | "SELL" = "BUY"): BrokerOrderRequest {
162
+ const initial = broker.getTradingCapabilities?.(instance, contract);
163
+ const orderType = initial?.orderTypes.includes("LMT") ? "LMT" : initial?.orderTypes[0] ?? "LMT";
164
+ const capabilities = broker.getTradingCapabilities?.(instance, contract, orderType) ?? initial;
165
+ const tif = capabilities?.tif.includes("DAY") ? "DAY" : capabilities?.tif[0];
166
+ return { brokerInstanceId: instance.id, accountId, contract, action, orderType, quantity: defaultQuantity(capabilities), tif };
167
+ }
168
+
169
+ /**
170
+ * A spread on the underlying this ticket trades, on the ticket's profile.
171
+ * Throws when the broker takes no spread of this size or a leg is not a
172
+ * listed contract of the underlying.
173
+ */
174
+ export function newStrategyDraft(broker: BrokerAdapter, instance: BrokerInstanceConfig, underlying: BrokerContractRef, intent: StrategyIntent, accountId?: string): BrokerOrderRequest {
175
+ if (underlying.secType && !["STK", "ETF"].includes(underlying.secType.toUpperCase())) throw new Error("Open the spread from its underlying stock or fund.");
176
+ const contract = strategyContract({ ...underlying, brokerInstanceId: instance.id });
177
+ const capabilities = broker.getTradingCapabilities?.(instance, contract);
178
+ // Trading switched off still opens the spread, with the ticket's Enable trading; a broker that rejects the contract does not.
179
+ if (capabilities && !capabilities.orderTypes.length && capabilities.disabledReason) throw new Error(capabilities.disabledReason);
180
+ if (!capabilities || (capabilities.maxLegs ?? 0) < Math.max(2, intent.legs.length)) {
181
+ throw new Error(capabilities?.maxLegs ? `${broker.name} takes spreads of 2 to ${capabilities.maxLegs} legs.` : `${broker.name} does not take multi-leg orders here.`);
182
+ }
183
+ const legs = strategyOrderLegs(intent.legs, contract, contract.currency);
184
+ if (!legs) throw new Error(`Every leg must be a listed ${contract.symbol} option.`);
185
+ const draft = newTicketDraft(broker, instance, contract, accountId, intent.action);
186
+ return { ...draft, legs, quantity: intent.quantity };
187
+ }
188
+
189
+ export function editTicketDraft(broker: BrokerAdapter, instance: BrokerInstanceConfig, draft: BrokerOrderRequest, field: string, value: string | boolean): BrokerOrderRequest {
190
+ // Switching a spread's side is the opposite trade: every leg turns over.
191
+ // A debit is not a credit, so the price starts again from the quote.
192
+ if (field === "action" && draft.legs?.length) {
193
+ if (value === draft.action) return draft;
194
+ const { limitPrice: _debit, ...reversed } = reverseStrategy(draft);
195
+ return reversed;
196
+ }
197
+ const numeric = ["quantity", "limitPrice", "stopPrice"].includes(field);
198
+ const next = { ...draft, [field]: numeric ? value === "" ? field === "quantity" ? NaN : undefined : Number(value) : value };
199
+ if (field === "orderType") {
200
+ if (next.orderType !== "LMT" && next.orderType !== "STP LMT") delete next.limitPrice;
201
+ if (next.orderType !== "STP" && next.orderType !== "STP LMT") delete next.stopPrice;
202
+ }
203
+ const capabilities = broker.getTradingCapabilities?.(instance, next.contract, next.orderType);
204
+ if (capabilities && !capabilities.tif.includes(next.tif ?? "DAY")) next.tif = capabilities.tif[0];
205
+ if (!capabilities?.extendedHours || (capabilities.extendedHoursTif && !capabilities.extendedHoursTif.includes(next.tif ?? "DAY"))) next.outsideRth = false;
206
+ return next;
207
+ }