gloomberb 0.17.1 → 0.17.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 (101) hide show
  1. package/package.json +1 -1
  2. package/src/{plugins/builtin/cloud → account}/auth-dialog.tsx +7 -7
  3. package/src/api-client/earnings.ts +4 -2
  4. package/src/app/global-shortcuts.ts +10 -0
  5. package/src/app/keybindings/actions.ts +16 -0
  6. package/src/app/runtime/desktop-deeplink.ts +1 -1
  7. package/src/app.tsx +1 -1
  8. package/src/architecture/core-builtin-allowlist.json +4 -9
  9. package/src/cli/commands/market.ts +34 -16
  10. package/src/cli/commands/options-chain.ts +29 -33
  11. package/src/cli/commands/options-leaps.ts +462 -63
  12. package/src/cli/commands/search.ts +10 -7
  13. package/src/cli/commands/ticker.ts +1 -1
  14. package/src/cli/known-gaps.ts +22 -0
  15. package/src/cli/pane-functions/catalog.ts +6 -3
  16. package/src/cli/pane-functions/dom.ts +17 -2
  17. package/src/cli/pane-functions/function-help.ts +1 -1
  18. package/src/cli/pane-functions/index.ts +5 -3
  19. package/src/cli/pane-functions/report-notices.ts +15 -0
  20. package/src/cli/symbol-args.ts +19 -0
  21. package/src/components/command-bar/workflow/builtin.ts +2 -2
  22. package/src/components/command-bar/workflow/collection-actions.ts +1 -1
  23. package/src/components/feedback-dialog.tsx +1 -1
  24. package/src/components/layout/shell/index.tsx +1 -1
  25. package/src/components/layout/status-bar-groups.ts +1 -1
  26. package/src/components/layout/status-bar.tsx +2 -2
  27. package/src/components/markdown-text.tsx +14 -2
  28. package/src/components/reader/article-reader.tsx +1 -1
  29. package/src/components/reader/detail-reader.tsx +10 -7
  30. package/src/components/reader/reader-text.tsx +32 -17
  31. package/src/layout-marketplace/gallery.tsx +2 -2
  32. package/src/layout-marketplace/linked-sync.ts +1 -1
  33. package/src/market-data/market/freshness.ts +3 -0
  34. package/src/market-data/published-apac-sessions.ts +12 -2
  35. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  36. package/src/plugins/builtin/chart-composer/presets.ts +33 -1
  37. package/src/plugins/builtin/chat/channel-labels.ts +2 -2
  38. package/src/plugins/builtin/chat/content/channel-navigation.ts +1 -1
  39. package/src/plugins/builtin/chat/content/index.tsx +2 -2
  40. package/src/plugins/builtin/chat/conversations.ts +1 -1
  41. package/src/plugins/builtin/chat/index.tsx +2 -2
  42. package/src/plugins/builtin/chat/share/flow.tsx +1 -1
  43. package/src/plugins/builtin/chat/sidebar-rows.ts +1 -1
  44. package/src/plugins/builtin/chat/sidebar.tsx +2 -2
  45. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +2 -2
  46. package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
  47. package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
  48. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +1 -1
  49. package/src/plugins/builtin/cloud/team/acm-tab.tsx +3 -3
  50. package/src/plugins/builtin/cloud/team/capability.ts +2 -2
  51. package/src/plugins/builtin/cloud/team/command.ts +4 -4
  52. package/src/plugins/builtin/cloud/team/module.tsx +3 -3
  53. package/src/plugins/builtin/cloud/team/pane-model.ts +2 -2
  54. package/src/plugins/builtin/cloud/team/pane-sections.tsx +1 -1
  55. package/src/plugins/builtin/cloud/team/pane-ui.tsx +1 -1
  56. package/src/plugins/builtin/cloud/team/pane.tsx +3 -3
  57. package/src/plugins/builtin/cloud/team/status-widget.tsx +3 -3
  58. package/src/plugins/builtin/cloud/team/team-state-host.ts +1 -1
  59. package/src/plugins/builtin/cloud/team/views.ts +2 -2
  60. package/src/plugins/builtin/cloud/thesis/board-pane.tsx +1 -1
  61. package/src/plugins/builtin/cloud/thesis/detail.tsx +1 -1
  62. package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +1 -1
  63. package/src/plugins/builtin/cloud/upgrade-command.ts +1 -1
  64. package/src/plugins/builtin/custom-view/pane.tsx +2 -2
  65. package/src/plugins/builtin/dividend-yield/headless.ts +8 -2
  66. package/src/plugins/builtin/dividend-yield/pane.tsx +4 -2
  67. package/src/plugins/builtin/docs/blocks.tsx +13 -2
  68. package/src/plugins/builtin/docs/pane.tsx +6 -5
  69. package/src/plugins/builtin/earnings/board-model.ts +18 -1
  70. package/src/plugins/builtin/earnings/board-table.ts +18 -3
  71. package/src/plugins/builtin/earnings/headless.ts +13 -2
  72. package/src/plugins/builtin/earnings/history-model.ts +1 -1
  73. package/src/plugins/builtin/earnings/history-table.ts +2 -2
  74. package/src/plugins/builtin/help/feedback-tab.tsx +1 -1
  75. package/src/plugins/builtin/notes/index.tsx +1 -1
  76. package/src/plugins/builtin/notes/owner.tsx +2 -2
  77. package/src/plugins/builtin/options/carry.ts +36 -0
  78. package/src/plugins/builtin/options/open-interest-as-of.ts +16 -5
  79. package/src/plugins/builtin/options/view.tsx +19 -7
  80. package/src/plugins/builtin/options-calculator/pane.tsx +56 -1
  81. package/src/plugins/builtin/options-calculator/premium-history.ts +34 -0
  82. package/src/plugins/builtin/portfolio-list/mutations.ts +1 -1
  83. package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +1 -1
  84. package/src/plugins/builtin/research/corporate-actions-pane.tsx +4 -2
  85. package/src/plugins/builtin/research/event-model.ts +7 -4
  86. package/src/plugins/builtin/series-inventory.ts +1 -0
  87. package/src/plugins/builtin/shared/earnings-report.ts +10 -4
  88. package/src/renderers/dom/styles.css +4 -2
  89. package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
  90. package/src/{plugins/builtin/cloud/team → team}/collections-sync.ts +6 -6
  91. package/src/{plugins/builtin/cloud/team → team}/collections.ts +3 -3
  92. package/src/{plugins/builtin/cloud/team → team}/model.ts +2 -2
  93. package/src/{plugins/builtin/cloud/team → team}/pane-request.ts +1 -1
  94. package/src/{plugins/builtin/cloud/team → team}/store.ts +3 -3
  95. package/src/types/data-provider.ts +3 -0
  96. package/src/types/financials.ts +6 -0
  97. package/src/utils/decimal-comma.ts +18 -0
  98. package/src/utils/exchanges.ts +7 -0
  99. package/src/utils/forward-dividend.ts +39 -9
  100. package/src/utils/option-expiry.ts +16 -0
  101. package/src/utils/sec.ts +2 -1
@@ -4,12 +4,12 @@ import {
4
4
  type TeamCollection,
5
5
  type TeamCollectionEvent,
6
6
  type TeamCollectionItem,
7
- } from "../../../../api-client";
8
- import type { AppTickerRepositoryPort } from "../../../../core/app-service-ports";
9
- import type { AppAction, AppState } from "../../../../core/state/app/types";
10
- import { useAppDispatch, useAppSelector, useAppStateRef } from "../../../../state/app/context";
11
- import type { TickerRecord } from "../../../../types/ticker";
12
- import { debugLog } from "../../../../utils/debug-log";
7
+ } from "../api-client";
8
+ import type { AppTickerRepositoryPort } from "../core/app-service-ports";
9
+ import type { AppAction, AppState } from "../core/state/app/types";
10
+ import { useAppDispatch, useAppSelector, useAppStateRef } from "../state/app/context";
11
+ import type { TickerRecord } from "../types/ticker";
12
+ import { debugLog } from "../utils/debug-log";
13
13
  import {
14
14
  applyMembership,
15
15
  blankTickerRecord,
@@ -1,6 +1,6 @@
1
- import type { TeamCollection, TeamCollectionItem } from "../../../../api-client";
2
- import type { AppConfig } from "../../../../types/config";
3
- import type { Portfolio, TickerRecord, Watchlist } from "../../../../types/ticker";
1
+ import type { TeamCollection, TeamCollectionItem } from "../api-client";
2
+ import type { AppConfig } from "../types/config";
3
+ import type { Portfolio, TickerRecord, Watchlist } from "../types/ticker";
4
4
 
5
5
  const TEAM_COLLECTION_PREFIX = "team:";
6
6
 
@@ -4,8 +4,8 @@ import type {
4
4
  TeamRole,
5
5
  TeamSummary,
6
6
  TeamUser,
7
- } from "../../../../api-client";
8
- import { blendHex, colors } from "../../../../theme/colors";
7
+ } from "../api-client";
8
+ import { blendHex, colors } from "../theme/colors";
9
9
 
10
10
  const TEAM_CHANNEL_PREFIX = "team:";
11
11
 
@@ -1,4 +1,4 @@
1
- import { createPaneRequestChannel } from "../../shared/pane-request";
1
+ import { createPaneRequestChannel } from "../plugins/builtin/shared/pane-request";
2
2
 
3
3
  export type TeamPaneSection = "members" | "invites" | "channels" | "settings";
4
4
 
@@ -4,9 +4,9 @@ import {
4
4
  type TeamReceivedInvitation,
5
5
  type TeamSummary,
6
6
  type TeamUpdatedEvent,
7
- } from "../../../../api-client";
8
- import type { AppNotificationDelivery, AppNotificationRequest, PluginPersistence } from "../../../../types/plugin";
9
- import { markNotificationLogReadByRef } from "../../../../notifications/notification-log";
7
+ } from "../api-client";
8
+ import type { AppNotificationDelivery, AppNotificationRequest, PluginPersistence } from "../types/plugin";
9
+ import { markNotificationLogReadByRef } from "../notifications/notification-log";
10
10
  import { describeTeamNotification, findTeam, teamIdFromChannelId, teamNotificationRefId } from "./model";
11
11
 
12
12
  export interface TeamStoreSnapshot {
@@ -2,6 +2,7 @@ import type { ExchangeRateSnapshot } from "./exchange-rate";
2
2
  import type {
3
3
  AnalystResearchData,
4
4
  CorporateActionsData,
5
+ EarningsEpsBasis,
5
6
  Quote,
6
7
  QuoteMetadata,
7
8
  TickerFinancials,
@@ -94,6 +95,8 @@ export interface EarningsEvent {
94
95
  epsRevisionDown7d?: number | null;
95
96
  epsRevisionDown30d?: number | null;
96
97
  epsActual: number | null;
98
+ /** "adjusted" when the estimate is the consensus on the adjusted basis the company's reports are on (a REIT's FFO), not a GAAP forecast. */
99
+ epsBasis?: EarningsEpsBasis | null;
97
100
  revenueEstimate: number | null;
98
101
  revenueLow?: number | null;
99
102
  revenueHigh?: number | null;
@@ -870,4 +870,10 @@ export interface OptionsChain {
870
870
  realtimeEligible?: boolean;
871
871
  /** ISO timestamp for the upstream options snapshot. */
872
872
  asOf?: string;
873
+ /**
874
+ * The session the chain's open interest counts, YYYY-MM-DD, as Gloom Cloud
875
+ * states it; null when none of its counts is dated. Absent from a source
876
+ * that does not say (an older server, another provider).
877
+ */
878
+ openInterestAsOf?: string | null;
873
879
  }
@@ -18,6 +18,24 @@ export function numberErrorMessage(message: string, value: unknown): string {
18
18
  return dotted == null ? message : `${message.replace(/\.$/, "")}; use a dot: ${dotted}`;
19
19
  }
20
20
 
21
+ const DECIMAL_COMMA_RANGE = /^([+-]?\d+(?:,\d+)?)\s*(-|to|\.\.)\s*([+-]?\d+(?:,\d+)?)$/i;
22
+
23
+ /**
24
+ * A range typed with decimal commas, `0,80-0,90`, as the dotted range to type
25
+ * instead (`0.80-0.90`); null when neither end has a decimal comma.
26
+ */
27
+ function dottedDecimalRange(value: unknown): string | null {
28
+ const match = DECIMAL_COMMA_RANGE.exec(typeof value === "string" ? value.trim() : "");
29
+ if (!match || !`${match[1]}${match[3]}`.includes(",")) return null;
30
+ return `${match[1]!.replace(",", ".")}${match[2]}${match[3]!.replace(",", ".")}`;
31
+ }
32
+
33
+ /** `numberErrorMessage` for a range of two numbers: "... got "0,80-0,90"; use a dot: 0.80-0.90". */
34
+ export function rangeErrorMessage(message: string, value: unknown): string {
35
+ const dotted = dottedDecimalRange(value);
36
+ return dotted == null ? message : `${message.replace(/\.$/, "")}; use a dot: ${dotted}`;
37
+ }
38
+
21
39
  /** What a number pasted from a sheet reads as. */
22
40
  export type PastedNumber =
23
41
  | { kind: "number"; value: number }
@@ -104,6 +104,11 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
104
104
  XPRA: "PSE",
105
105
  VIE: "VIE",
106
106
  XWBO: "VIE",
107
+ // The Vienna MTF, Wiener Boerse's multilateral trading facility, which Gloom
108
+ // Cloud names by its segment MIC. Its ETPs and notes are its own listings.
109
+ WBDM: "WBDM",
110
+ "VIENNA MTF": "WBDM",
111
+ VMTF: "WBDM",
107
112
  TPE: "TWSE",
108
113
  TWSE: "TWSE",
109
114
  TAI: "TWSE",
@@ -276,6 +281,7 @@ export const EXCHANGE_TIME_ZONES: Record<string, string> = {
276
281
  WSE: "Europe/Warsaw",
277
282
  PSE: "Europe/Prague",
278
283
  VIE: "Europe/Vienna",
284
+ WBDM: "Europe/Vienna",
279
285
  TPEX: "Asia/Taipei",
280
286
  TWSE: "Asia/Taipei",
281
287
  NSE: "Asia/Kolkata",
@@ -347,6 +353,7 @@ const EXCHANGE_NAMES: Record<string, string> = {
347
353
  WSE: "Warsaw Stock Exchange",
348
354
  PSE: "Prague Stock Exchange",
349
355
  VIE: "Vienna Stock Exchange",
356
+ WBDM: "Vienna MTF",
350
357
  TWSE: "Taiwan Stock Exchange",
351
358
  TPEX: "Taipei Exchange",
352
359
  NSE: "National Stock Exchange of India",
@@ -1,14 +1,44 @@
1
1
  /**
2
2
  * What a forward dividend yield is everywhere it shows (fundamentals, DVD):
3
- * the indicated annual rate Gloom Cloud serves, the latest regular
4
- * distribution annualized, over the price. It is dated by that
5
- * distribution's ex-date.
3
+ * the indicated annual rate Gloom Cloud serves over the price, dated by the
4
+ * ex-date of the distribution it annualizes. While no other is announced that
5
+ * is the latest distribution, whose ex-date has passed. Once the next one is
6
+ * declared the rate annualizes it, and its ex-date is still ahead: the basis
7
+ * then names the declared distribution by its ex-date and pay date, never a
8
+ * rate "as of" a day that has not come.
6
9
  */
7
- const FORWARD_DIVIDEND_BASIS = "latest distribution annualized";
10
+ const LATEST_DISTRIBUTION = "latest distribution annualized";
8
11
 
9
- /** `latest distribution annualized, as of 2026-10-01`; the basis alone when the date is unknown. */
10
- export function forwardDividendBasis(asOf: string | Date | null | undefined): string {
11
- const text = asOf instanceof Date ? (Number.isFinite(asOf.getTime()) ? asOf.toISOString() : "") : asOf ?? "";
12
- const date = /^\d{4}-\d{2}-\d{2}/.test(text) ? text.slice(0, 10) : null;
13
- return date ? `${FORWARD_DIVIDEND_BASIS}, as of ${date}` : FORWARD_DIVIDEND_BASIS;
12
+ /** `2026-10-01` from a date or an ISO text; null when it is neither. */
13
+ function isoDay(value: string | Date | null | undefined): string | null {
14
+ const text = value instanceof Date ? (Number.isFinite(value.getTime()) ? value.toISOString() : "") : value ?? "";
15
+ return /^\d{4}-\d{2}-\d{2}/.test(text) ? text.slice(0, 10) : null;
16
+ }
17
+
18
+ /** A rate dated by an ex-date after today annualizes a distribution declared and not yet gone ex. */
19
+ function isDeclared(exDate: string | null, now: Date): exDate is string {
20
+ return exDate != null && exDate > now.toISOString().slice(0, 10);
21
+ }
22
+
23
+ /**
24
+ * `latest distribution annualized, as of 2026-08-20`, or for a rate dated
25
+ * ahead, `annualized from the declared distribution payable 2026-12-10, ex
26
+ * 2026-11-19`; the basis alone when the date is unknown. `payDate` is the pay
27
+ * date stated beside that ex-date, for a source that pairs them.
28
+ */
29
+ export function forwardDividendBasis(
30
+ asOf: string | Date | null | undefined,
31
+ options: { payDate?: string | Date | null; now?: Date } = {},
32
+ ): string {
33
+ const exDate = isoDay(asOf);
34
+ if (!isDeclared(exDate, options.now ?? new Date())) return exDate ? `${LATEST_DISTRIBUTION}, as of ${exDate}` : LATEST_DISTRIBUTION;
35
+ const payDate = isoDay(options.payDate);
36
+ return `annualized from the declared distribution${payDate ? ` payable ${payDate}` : ""}, ex ${exDate}`;
37
+ }
38
+
39
+ /** The same basis as a pane's short note under the figure: `as of 2026-08-20`, or `declared` beside the next ex-date it shows. */
40
+ export function forwardDividendNote(asOf: string | Date | null | undefined, now = new Date()): string | null {
41
+ const exDate = isoDay(asOf);
42
+ if (!exDate) return null;
43
+ return isDeclared(exDate, now) ? "declared" : `as of ${exDate}`;
14
44
  }
@@ -124,3 +124,19 @@ export function missingExpiryText(requested: number, expirations: readonly numbe
124
124
  ? `no option contracts returned for${subject ? ` ${subject}` : ""} expiry ${date}; nearest others: ${choices}`
125
125
  : `no expiry ${date}${of}; available: ${choices}`;
126
126
  }
127
+
128
+ /**
129
+ * How a command fails on an expiry the chain cannot show: `options --expiration`
130
+ * and `fn OMON --expiration` print the same error, with the command that lists
131
+ * every expiry as its advice line.
132
+ */
133
+ export function missingExpiryMessage(
134
+ symbol: string,
135
+ requested: number,
136
+ expirations: readonly number[],
137
+ ): { message: string; details: string } {
138
+ return {
139
+ message: missingExpiryText(requested, expirations, symbol),
140
+ details: `Run gloomberb options ${symbol} to list every expiry (--json carries them too).`,
141
+ };
142
+ }
package/src/utils/sec.ts CHANGED
@@ -37,7 +37,8 @@ function isEquityType(value?: string): boolean {
37
37
 
38
38
  const FUND_TYPES = new Set(["ETF", "ETP", "FUND", "MUTUALFUND", "CEF", "CLOSEDEND", "CLOSEDENDFUND"]);
39
39
 
40
- function isFundType(value?: string): boolean {
40
+ /** An instrument type that names a fund (ETF, closed-end fund), which reports no earnings of its own. */
41
+ export function isFundType(value?: string): boolean {
41
42
  return FUND_TYPES.has(normalize(value).replace(/[\s_-]/g, ""));
42
43
  }
43
44