gloomberb 0.19.0 → 0.19.1
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.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +23 -1
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/data.ts +2 -1
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +4 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +12 -0
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/cli/commands/market.ts +35 -12
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/overview.ts +86 -20
- package/src/cli/commands/ticker.ts +32 -2
- package/src/cli/errors.ts +17 -2
- package/src/cli/glossary.ts +6 -0
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/listing-arg.ts +57 -10
- package/src/cli/pane-functions/capabilities.ts +1 -0
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/freshness.ts +7 -1
- package/src/cli/pane-functions/function-help.ts +7 -0
- package/src/cli/pane-functions/index.ts +6 -3
- package/src/cli/quote-answer.ts +1 -1
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/ticker/list-table-view.tsx +4 -20
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/docs/snapshot.json +32 -5
- package/src/i18n/es.ts +2 -4
- package/src/i18n/ja.ts +2 -4
- package/src/i18n/ko.ts +2 -4
- package/src/i18n/zh-cn.ts +2 -4
- package/src/i18n/zh-tw.ts +2 -4
- package/src/market-data/depositary-pair.ts +25 -1
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +7 -1
- package/src/news/types.ts +6 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli.ts +34 -11
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +15 -6
- package/src/plugins/builtin/alerts/headless.ts +10 -6
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/index.tsx +4 -5
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -22
- package/src/plugins/builtin/clinical-trials/pane.tsx +46 -27
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +87 -9
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/pane.tsx +3 -0
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/credit-documents/headless.ts +3 -3
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/headless.ts +13 -3
- package/src/plugins/builtin/earnings-calls/pane.tsx +7 -0
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +10 -2
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +3 -0
- package/src/plugins/builtin/help/index.tsx +61 -59
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/headless.ts +4 -2
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/market-movers/client.ts +2 -0
- package/src/plugins/builtin/market-movers/headless.ts +3 -1
- package/src/plugins/builtin/market-movers/screener.ts +8 -4
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +3 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +2 -6
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +2 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +4 -6
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +9 -7
- package/src/plugins/builtin/research/event-model.ts +30 -4
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +8 -25
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +52 -15
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/live-headless.ts +21 -2
- package/src/plugins/builtin/sec/headless.ts +2 -1
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +13 -5
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/report-freshness.ts +8 -0
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/overview/model.ts +10 -2
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -6
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/volatility/pane.tsx +5 -2
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/portfolio/column-values.ts +11 -7
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +9 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +8 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/news-source.ts +2 -0
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +42 -8
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +6 -1
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +27 -0
- package/src/types/headless.ts +6 -0
- package/src/utils/crypto-pair.ts +121 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/fundamentals.ts +36 -5
- package/src/version.ts +2 -2
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ The script downloads the prebuilt release from GitHub and checks its SHA-256 aga
|
|
|
50
50
|
With [Bun](https://bun.sh), on any supported OS:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
bun install -g
|
|
53
|
+
bun install -g @gloom-sh/terminal
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Then run `gloom`. The command was called `gloomberb` before 0.18.4, and that name still works. For chart graphics, use a Kitty-compatible terminal such as Ghostty, Kitty, or WezTerm. Windows Terminal draws charts as text; the desktop app draws them in full.
|
package/README.zh-CN.md
CHANGED
|
@@ -62,7 +62,7 @@ curl -fsSL gloom.sh/install | bash
|
|
|
62
62
|
也可在 macOS、Linux 或 Windows x64 上通过 [Bun](https://bun.sh) 安装:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
bun install -g
|
|
65
|
+
bun install -g @gloom-sh/terminal
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
运行 `gloom` 即可启动。0.18.4 之前命令名为 `gloomberb`,该名称仍可使用。要显示图形,请使用兼容 Kitty 图形协议的终端,例如 Ghostty、Kitty 或 WezTerm。直接下载、安装位置与更新方式详见[安装指南(英文)](docs/installation.md)。
|
package/package.json
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
diff --git a/dist/api/browser/index.ts b/dist/api/browser/index.ts
|
|
2
|
+
index 3e309b18fdbcec84a4a56f97df4debe7fb8246f8..03420222c1d9ba897835c5e7376c7153ede294fc 100644
|
|
3
|
+
--- a/dist/api/browser/index.ts
|
|
4
|
+
+++ b/dist/api/browser/index.ts
|
|
5
|
+
@@ -50,7 +50,7 @@ class Electroview<T extends RPCWithTransport> {
|
|
6
|
+
// Note: Using ws:// for localhost is intentional - all RPC messages are
|
|
7
|
+
// encrypted with per-webview AES-GCM keys, making TLS redundant
|
|
8
|
+
const socket = new WebSocket(
|
|
9
|
+
- `ws://localhost:${RPC_SOCKET_PORT}/socket?webviewId=${WEBVIEW_ID}`,
|
|
10
|
+
+ `ws://127.0.0.1:${RPC_SOCKET_PORT}/socket?webviewId=${WEBVIEW_ID}`,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
this.bunSocket = socket;
|
|
1
14
|
diff --git a/dist/api/bun/core/BrowserWindow.ts b/dist/api/bun/core/BrowserWindow.ts
|
|
2
15
|
index c26c2c977ee1fc25a8dc144b85bac8626a45f572..8920a2905380228c23cba66c20d9bfd19ee13e02 100644
|
|
3
16
|
--- a/dist/api/bun/core/BrowserWindow.ts
|
|
@@ -54,7 +67,7 @@ index c26c2c977ee1fc25a8dc144b85bac8626a45f572..8920a2905380228c23cba66c20d9bfd1
|
|
|
54
67
|
width: this.frame.width,
|
|
55
68
|
height: this.frame.height,
|
|
56
69
|
diff --git a/dist/api/bun/core/Socket.ts b/dist/api/bun/core/Socket.ts
|
|
57
|
-
index cdaa2bca0fabe577cb667056aa6f8f84211189c2..
|
|
70
|
+
index cdaa2bca0fabe577cb667056aa6f8f84211189c2..e9cdd82c934f6da58055dc8343ee3b953ec1ee81 100644
|
|
58
71
|
--- a/dist/api/bun/core/Socket.ts
|
|
59
72
|
+++ b/dist/api/bun/core/Socket.ts
|
|
60
73
|
@@ -3,13 +3,8 @@ import { BrowserView } from "./BrowserView";
|
|
@@ -73,6 +86,15 @@ index cdaa2bca0fabe577cb667056aa6f8f84211189c2..3839e5232742c2467180c344f51a30f1
|
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
// Encrypt function
|
|
89
|
+
@@ -65,6 +60,8 @@ const startRPCServer = () => {
|
|
90
|
+
while (port <= endPort) {
|
|
91
|
+
try {
|
|
92
|
+
server = Bun.serve<{ webviewId: number }>({
|
|
93
|
+
+ // Only this machine's webviews connect.
|
|
94
|
+
+ hostname: "127.0.0.1",
|
|
95
|
+
port,
|
|
96
|
+
fetch(req: Request, server: Server<{ webviewId: number }>) {
|
|
97
|
+
const url = new URL(req.url);
|
|
76
98
|
diff --git a/dist/api/bun/core/Updater.ts b/dist/api/bun/core/Updater.ts
|
|
77
99
|
index 39daf6d341780af8e30c2c50327162863f5977ec..4f3d2bef8e4b41a13a5a522c0af8dd700879a5db 100644
|
|
78
100
|
--- a/dist/api/bun/core/Updater.ts
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from"
|
|
1
2
|
type AppStore = "app-store" | "google-play"
|
|
2
3
|
export type AppChart = "free" | "paid" | "grossing" | "unranked"
|
|
3
4
|
|
|
@@ -97,6 +98,8 @@ interface AppSourceState {
|
|
|
97
98
|
export interface AppAttentionPayload {
|
|
98
99
|
generatedAt: string
|
|
99
100
|
requestedSymbol?: string
|
|
101
|
+
/** The company's home line the data is read from, when not the listing asked (a receipt, a foreign line). */
|
|
102
|
+
companyFrom?: CompanyFrom
|
|
100
103
|
page?: { offset: number; limit: number; total: number; nextOffset: number | null }
|
|
101
104
|
symbol: string | null
|
|
102
105
|
access: "pro" | "preview"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from";
|
|
1
2
|
/**
|
|
2
3
|
* Schedule 13D and 13G beneficial ownership reports about one issuer, as
|
|
3
4
|
* `GET /cloud/sec/beneficial-owners` returns them. Percentages are percent
|
|
@@ -71,6 +72,8 @@ export interface BeneficialOwnersCoverage {
|
|
|
71
72
|
|
|
72
73
|
export interface BeneficialOwnersPayload {
|
|
73
74
|
ticker: string;
|
|
75
|
+
/** The company's home line the owners are read from, when not the listing asked (a foreign line). */
|
|
76
|
+
companyFrom?: CompanyFrom;
|
|
74
77
|
cik: string;
|
|
75
78
|
companyName: string;
|
|
76
79
|
/** When the server assembled the list, an ISO time. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from"
|
|
1
2
|
import { listingAbroad, type IssuerListingParams } from "./paths";
|
|
2
3
|
|
|
3
4
|
type QuoteMatchMode = "exact" | "whitespace" | "nfkc_whitespace";
|
|
@@ -132,6 +133,8 @@ export interface KpiSeries {
|
|
|
132
133
|
}
|
|
133
134
|
export interface KpisPayload {
|
|
134
135
|
symbol: string
|
|
136
|
+
/** The company's home line the data is read from, when not the listing asked (a receipt, a foreign line). */
|
|
137
|
+
companyFrom?: CompanyFrom
|
|
135
138
|
status: "available" | "unavailable"
|
|
136
139
|
asOf: string | null
|
|
137
140
|
coverage: KpiCoverage
|
package/src/api-client/data.ts
CHANGED
|
@@ -1284,8 +1284,9 @@ export class CloudDataApi {
|
|
|
1284
1284
|
query: string,
|
|
1285
1285
|
limit?: number,
|
|
1286
1286
|
topics?: readonly string[],
|
|
1287
|
+
options: { closest?: boolean; tickers?: readonly string[] } = {},
|
|
1287
1288
|
): Promise<CloudNewsListResponse> {
|
|
1288
|
-
return this.request<CloudNewsListResponse>(cloudNewsSearchPath(query, limit, topics));
|
|
1289
|
+
return this.request<CloudNewsListResponse>(cloudNewsSearchPath(query, limit, topics, options));
|
|
1289
1290
|
}
|
|
1290
1291
|
|
|
1291
1292
|
async getCloudNewsStory(storyId: string): Promise<CloudNewsPayload> {
|
package/src/api-client/hiring.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from"
|
|
1
2
|
/** HIRING is an observed requisition series, not headcount or job creation. */
|
|
2
3
|
interface HiringCompany {
|
|
3
4
|
entityId: string
|
|
@@ -76,6 +77,8 @@ export interface HiringSummary extends HiringCompany {
|
|
|
76
77
|
export interface HiringPayload extends HiringSummary {
|
|
77
78
|
generatedAt: string
|
|
78
79
|
requestedSymbol?: string
|
|
80
|
+
/** The company's home line the data is read from, when not the listing asked (a receipt, a foreign line). */
|
|
81
|
+
companyFrom?: CompanyFrom
|
|
79
82
|
proRequired: true
|
|
80
83
|
preview: boolean
|
|
81
84
|
locked: { history: number; evidence: number; peers: number; signals: number; mix: number }
|
package/src/api-client/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { CloudApiRequestTransport, putWithRevision } from "./request";
|
|
|
24
24
|
import { CloudApiSocket } from "./socket";
|
|
25
25
|
import { CloudCollectionsApi } from "./collections";
|
|
26
26
|
import { CloudFeedbackApi } from "./feedback";
|
|
27
|
+
import { CloudRoadmapApi } from "./roadmap";
|
|
27
28
|
import { CloudNotesApi } from "./notes";
|
|
28
29
|
import { CloudTeamsApi } from "./teams";
|
|
29
30
|
import { CloudTelemetryApi } from "./telemetry";
|
|
@@ -154,6 +155,7 @@ class GloomApiClient {
|
|
|
154
155
|
private readonly data: CloudDataApi = new CloudDataApi((path, options) => this.request(path, options));
|
|
155
156
|
private readonly notes: CloudNotesApi = new CloudNotesApi((path, options) => this.request(path, options));
|
|
156
157
|
private readonly feedback: CloudFeedbackApi = new CloudFeedbackApi((path, options) => this.request(path, options));
|
|
158
|
+
private readonly roadmap: CloudRoadmapApi = new CloudRoadmapApi((path, options) => this.request(path, options));
|
|
157
159
|
private readonly telemetry: CloudTelemetryApi = new CloudTelemetryApi((path, options) => this.request(path, options));
|
|
158
160
|
private readonly theses: CloudThesesApi = new CloudThesesApi((path, options) => this.request(path, options));
|
|
159
161
|
private readonly collections: CloudCollectionsApi = new CloudCollectionsApi((path, options) => this.request(path, options));
|
|
@@ -874,6 +876,8 @@ class GloomApiClient {
|
|
|
874
876
|
putCloudNote = this.notes.putNote.bind(this.notes);
|
|
875
877
|
submitFeedback = this.feedback.submitFeedback.bind(this.feedback);
|
|
876
878
|
listFeedback = this.feedback.listFeedback.bind(this.feedback);
|
|
879
|
+
getRoadmap = this.roadmap.getRoadmap.bind(this.roadmap);
|
|
880
|
+
setRoadmapVote = this.roadmap.setRoadmapVote.bind(this.roadmap);
|
|
877
881
|
reportCrashErrors = this.telemetry.reportCrashErrors.bind(this.telemetry);
|
|
878
882
|
reportUsageCounts = this.telemetry.reportUsageCounts.bind(this.telemetry);
|
|
879
883
|
reportAttentionCounts = this.telemetry.reportAttentionCounts.bind(this.telemetry);
|
package/src/api-client/ipo.ts
CHANGED
|
@@ -28,6 +28,24 @@ interface IpoFirstDay {
|
|
|
28
28
|
returnPct: number | null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* When a US IPO's lock-up is estimated to end: the final prospectus's date
|
|
33
|
+
* plus the period it states, or the first session plus the usual 180 days
|
|
34
|
+
* until the prospectus is read. An estimate: underwriters can release
|
|
35
|
+
* shares early, and some holders agree to other periods.
|
|
36
|
+
*/
|
|
37
|
+
export interface IpoLockup {
|
|
38
|
+
/** YYYY-MM-DD. */
|
|
39
|
+
endsOn: string;
|
|
40
|
+
days: number;
|
|
41
|
+
/** `prospectus`: the period the final prospectus states; `typical`: 180 days, when it states none of a lock-up's length or was not read. */
|
|
42
|
+
basis: "prospectus" | "typical";
|
|
43
|
+
/** The date the final prospectus is dated, when it was read. */
|
|
44
|
+
prospectusDate: string | null;
|
|
45
|
+
/** The SEC document the period was read from. */
|
|
46
|
+
prospectusUrl: string | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
export interface IpoDeal {
|
|
32
50
|
/** Assigned when the deal is first seen and never changed. */
|
|
33
51
|
id: string;
|
|
@@ -66,6 +84,8 @@ export interface IpoDeal {
|
|
|
66
84
|
listingType: IpoListingType;
|
|
67
85
|
filedDate: string | null;
|
|
68
86
|
firstDay: IpoFirstDay | null;
|
|
87
|
+
/** A priced or listed US IPO's estimated lock-up end; absent elsewhere, and from a server without the estimate. */
|
|
88
|
+
lockup?: IpoLockup | null;
|
|
69
89
|
updatedAt: string;
|
|
70
90
|
}
|
|
71
91
|
|
|
@@ -86,11 +106,14 @@ export interface IpoCalendarPayload {
|
|
|
86
106
|
|
|
87
107
|
/**
|
|
88
108
|
* Narrows or moves the window. Without any, the server sends its default:
|
|
89
|
-
*
|
|
109
|
+
* a year back (90 days on a server before that) to 180 days ahead, filed
|
|
110
|
+
* and withdrawn deals left out.
|
|
90
111
|
*/
|
|
91
112
|
export interface IpoCalendarParams {
|
|
92
113
|
region?: IpoRegion;
|
|
93
114
|
status?: IpoStatus;
|
|
115
|
+
/** Company (in either script) or symbol the deals' names contain. */
|
|
116
|
+
q?: string;
|
|
94
117
|
/** First listing date, YYYY-MM-DD. */
|
|
95
118
|
from?: string;
|
|
96
119
|
/** Last listing date, YYYY-MM-DD. */
|
package/src/api-client/mna.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from";
|
|
1
2
|
/**
|
|
2
3
|
* Mergers and acquisitions read from news, press releases and SEC filings.
|
|
3
4
|
* A deal is one target and one buyer; a target with competing bidders has a
|
|
@@ -84,6 +85,8 @@ export interface MnaDealsParams {
|
|
|
84
85
|
|
|
85
86
|
export interface MnaDealsPayload {
|
|
86
87
|
deals: MnaDeal[];
|
|
88
|
+
/** The company's home line the data is read from, when not the listing asked (a receipt, a foreign line). */
|
|
89
|
+
companyFrom?: CompanyFrom;
|
|
87
90
|
hasMore: boolean;
|
|
88
91
|
nextOffset: number;
|
|
89
92
|
/** "delayed" hides deals first reported within `delayDays` from a free account. */
|
package/src/api-client/paths.ts
CHANGED
|
@@ -621,10 +621,18 @@ export function cloudNewsPath(params: CloudNewsParams = {}): string {
|
|
|
621
621
|
|
|
622
622
|
/** Stories whose headline, summary or publisher matches the text, best match first; the service takes at most 100. */
|
|
623
623
|
/** `topics` are NI codes the stories must carry: `query=gold&topics=metals` is gold's market news. */
|
|
624
|
-
|
|
624
|
+
/**
|
|
625
|
+
* `closest`: when no story has every word, the stories with the most of them, marked `match: "closest"`.
|
|
626
|
+
* `tickers`: only the stories filed under them (`HAWK:NYSE`).
|
|
627
|
+
*/
|
|
628
|
+
export function cloudNewsSearchPath(
|
|
629
|
+
query: string, limit?: number, topics?: readonly string[], options: { closest?: boolean; tickers?: readonly string[] } = {},
|
|
630
|
+
): string {
|
|
625
631
|
const search = new URLSearchParams({ query });
|
|
626
632
|
if (limit != null) search.set("limit", String(limit));
|
|
627
633
|
if (topics?.length) search.set("topics", topics.join(","));
|
|
634
|
+
if (options.tickers?.length) search.set("tickers", options.tickers.join(","));
|
|
635
|
+
if (options.closest) search.set("closest", "1");
|
|
628
636
|
return appendQuery("/news/search", search);
|
|
629
637
|
}
|
|
630
638
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { CloudApiRequest } from "./request";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The public roadmap: what is being built, what is planned and what shipped,
|
|
5
|
+
* with the votes Pro members put on the first two. Anyone reads it; a signed-in
|
|
6
|
+
* Pro member votes once per item and can take the vote back.
|
|
7
|
+
*/
|
|
8
|
+
const ROADMAP_STATUSES = ["building", "planned", "shipped"] as const;
|
|
9
|
+
export type RoadmapStatus = (typeof ROADMAP_STATUSES)[number];
|
|
10
|
+
|
|
11
|
+
export interface RoadmapItem {
|
|
12
|
+
id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
/** One line on what it is. */
|
|
15
|
+
blurb: string;
|
|
16
|
+
status: RoadmapStatus;
|
|
17
|
+
/** ISO time it shipped; null until then. */
|
|
18
|
+
shippedAt: string | null;
|
|
19
|
+
votes: number;
|
|
20
|
+
/** Whether this account voted for it; false signed out. */
|
|
21
|
+
voted: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RoadmapPayload {
|
|
25
|
+
items: RoadmapItem[];
|
|
26
|
+
/** The service's word on whether this session may vote (a verified Pro account). */
|
|
27
|
+
canVote: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RoadmapVote {
|
|
31
|
+
id: string;
|
|
32
|
+
votes: number;
|
|
33
|
+
voted: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
37
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseItem(value: unknown): RoadmapItem | null {
|
|
41
|
+
if (!isRecord(value)) return null;
|
|
42
|
+
const { id, title, blurb, status, shippedAt, votes, voted } = value;
|
|
43
|
+
if (typeof id !== "string" || !id || typeof title !== "string" || !title.trim()) return null;
|
|
44
|
+
if (!(ROADMAP_STATUSES as readonly unknown[]).includes(status)) return null;
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
title: title.trim(),
|
|
48
|
+
blurb: typeof blurb === "string" ? blurb.trim() : "",
|
|
49
|
+
status: status as RoadmapStatus,
|
|
50
|
+
shippedAt: typeof shippedAt === "string" && Number.isFinite(Date.parse(shippedAt)) ? shippedAt : null,
|
|
51
|
+
votes: typeof votes === "number" && Number.isFinite(votes) ? Math.max(0, Math.round(votes)) : 0,
|
|
52
|
+
voted: voted === true,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Items it cannot read (an unknown status from a newer service) are left out, not the whole list. */
|
|
57
|
+
function parseRoadmapPayload(value: unknown): RoadmapPayload {
|
|
58
|
+
if (!isRecord(value) || !Array.isArray(value.items)) throw new Error("The roadmap service returned invalid data.");
|
|
59
|
+
return {
|
|
60
|
+
items: value.items.map(parseItem).filter((item): item is RoadmapItem => item !== null),
|
|
61
|
+
canVote: value.canVote === true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseVote(value: unknown): RoadmapVote {
|
|
66
|
+
if (!isRecord(value) || typeof value.id !== "string" || typeof value.votes !== "number" || typeof value.voted !== "boolean") {
|
|
67
|
+
throw new Error("The roadmap service returned invalid data.");
|
|
68
|
+
}
|
|
69
|
+
return { id: value.id, votes: value.votes, voted: value.voted };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class CloudRoadmapApi {
|
|
73
|
+
constructor(private readonly request: CloudApiRequest) {}
|
|
74
|
+
|
|
75
|
+
async getRoadmap(signal?: AbortSignal): Promise<RoadmapPayload> {
|
|
76
|
+
return parseRoadmapPayload(await this.request<unknown>("/roadmap", { signal }));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Votes, or takes the vote back; the answer is the item's count after it. */
|
|
80
|
+
async setRoadmapVote(id: string, voted: boolean): Promise<RoadmapVote> {
|
|
81
|
+
return parseVote(await this.request<unknown>(`/roadmap/${encodeURIComponent(id)}/vote`, {
|
|
82
|
+
method: voted ? "POST" : "DELETE",
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/api-client/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from";
|
|
1
2
|
import type {
|
|
2
3
|
AnalystResearchData,
|
|
3
4
|
CorporateActionsData,
|
|
@@ -1060,6 +1061,8 @@ export interface CloudEarningsCallListPayload {
|
|
|
1060
1061
|
pending?: boolean;
|
|
1061
1062
|
/** Set when the requested symbol is not one the SEC knows. */
|
|
1062
1063
|
unknownTicker?: boolean;
|
|
1064
|
+
/** The company's home line the calls are read from, when not the listing asked (a receipt, a foreign line). */
|
|
1065
|
+
companyFrom?: CompanyFrom;
|
|
1063
1066
|
}
|
|
1064
1067
|
|
|
1065
1068
|
/** One row of a proxy statement's Summary Compensation Table, in dollars. */
|
|
@@ -1456,6 +1459,8 @@ export interface CloudSecFilingsResponse {
|
|
|
1456
1459
|
filings: CloudSecFilingPayload[];
|
|
1457
1460
|
hasMore: boolean;
|
|
1458
1461
|
nextOffset: number;
|
|
1462
|
+
/** The company's home line the filings are read from, when not the listing asked (a foreign line). */
|
|
1463
|
+
companyFrom?: CompanyFrom;
|
|
1459
1464
|
}
|
|
1460
1465
|
|
|
1461
1466
|
export interface CloudSecDocumentsResponse {
|
|
@@ -1558,6 +1563,13 @@ export interface CloudNewsListResponse {
|
|
|
1558
1563
|
nextCursor: string | null;
|
|
1559
1564
|
/** Which news the service answered with, when it says: the live wire, or the one held back for a free account. */
|
|
1560
1565
|
access?: "realtime" | "delayed";
|
|
1566
|
+
/**
|
|
1567
|
+
* A search asked for its closest stories: `all` when stories have every
|
|
1568
|
+
* word, `closest` when none does and these have the most of them.
|
|
1569
|
+
*/
|
|
1570
|
+
match?: "all" | "closest";
|
|
1571
|
+
/** A receipt's or foreign line's feed is its company's: the home line it reads, when not the listing asked. */
|
|
1572
|
+
companyFrom?: CompanyFrom;
|
|
1561
1573
|
}
|
|
1562
1574
|
|
|
1563
1575
|
interface CloudTweetUserPayload {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"src/cli/commands/options-chain.ts": ["options","options-calculator"],
|
|
10
10
|
"src/cli/commands/options-leaps.ts": ["iv-history","options","vol-surface"],
|
|
11
11
|
"src/cli/commands/overview.ts": ["correlation","econ","market-movers","sectors","world-indices"],
|
|
12
|
-
"src/cli/commands/ticker.ts": ["notes"],
|
|
12
|
+
"src/cli/commands/ticker.ts": ["ipo-calendar","notes"],
|
|
13
13
|
"src/cli/desktop-pane-shot.ts": ["market-valuation","time-sales"],
|
|
14
14
|
"src/cli/pane-functions/data.ts": ["chart-composer","correlation","seasonality","ticker-detail"],
|
|
15
15
|
"src/cli/pane-functions/financial-ratio-shot.ts": ["ticker-detail"],
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"src/components/layout/shell/index.tsx": ["cloud"],
|
|
28
28
|
"src/components/onboarding/use-onboarding-upgrade.ts": ["chat"],
|
|
29
29
|
"src/layout/desks.ts": ["chart-composer"],
|
|
30
|
-
"src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","commodities-energy","commodity-balances","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","crypto-etf-flows","debt-maturities","dividend-yield","docs","doe","earnings","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","fixed-income","futures","futures-curve","fx-matrix","gpu","help","holders","insider","ipo-calendar","iv-history","jobs","jodi","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","primary-dealers","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","thirteenf","ticker-detail","time-sales","trace-bonds","treasury-auctions","treasury-daily","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
|
|
30
|
+
"src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","commodities-energy","commodity-balances","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","crypto-etf-flows","debt-maturities","dividend-yield","docs","doe","earnings","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","fixed-income","futures","futures-curve","fx-matrix","gpu","help","holders","insider","ipo-calendar","iv-history","jobs","jodi","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","primary-dealers","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","roadmap","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","thirteenf","ticker-detail","time-sales","trace-bonds","treasury-auctions","treasury-daily","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
|
|
31
31
|
"src/plugins/catalog-ui.ts": ["alerts","clinical-trials","cloud","comment-letters","custom-view","fear-greed","ipo-calendar","market-halts","market-heatmap","members","news","notes","openfda","research-search"],
|
|
32
32
|
"src/plugins/catalog.ts": ["debug"],
|
|
33
33
|
"src/remote/controller.ts": ["custom-view"],
|
|
@@ -3,6 +3,7 @@ import type { CliCommandDef } from "../../types/plugin";
|
|
|
3
3
|
import { TIME_RANGES, type TimeRange } from "../../time-series/range";
|
|
4
4
|
import { yearToDateStartIndex } from "../../market-data/market/year-to-date";
|
|
5
5
|
import type { EarningsEvent, QuoteBatchResult, QuoteSubscriptionTarget, SecFilingItem } from "../../types/data-provider";
|
|
6
|
+
import { companyDataNotes, type CompanyFrom } from "../../types/company-from";
|
|
6
7
|
import type { NewsArticle, NewsFeed, NewsQuery } from "../../news/types";
|
|
7
8
|
import type {
|
|
8
9
|
AnalystResearchData,
|
|
@@ -165,6 +166,7 @@ import { bareFuturesCode, futuresBoardContract } from "../../market-data/futures
|
|
|
165
166
|
import { commodityNamedIn, commoditySearchName } from "../../market-data/commodity-names";
|
|
166
167
|
import { newsEmptyMessage } from "./news-hints";
|
|
167
168
|
import {
|
|
169
|
+
closestStoriesReason,
|
|
168
170
|
commodityNewsRoute,
|
|
169
171
|
commonWordCandidate,
|
|
170
172
|
METALS_NEWS_TOPIC,
|
|
@@ -179,6 +181,7 @@ import {
|
|
|
179
181
|
noMetalStoriesMessage,
|
|
180
182
|
noStoriesMessage,
|
|
181
183
|
quotedSearchCommand,
|
|
184
|
+
searchClosestNewsStories,
|
|
182
185
|
searchNewsStories,
|
|
183
186
|
wordMeansMore,
|
|
184
187
|
} from "./news-search";
|
|
@@ -189,7 +192,7 @@ import { exportedFundamentals } from "../../utils/price-earnings";
|
|
|
189
192
|
import { fundamentalsFreshness, fxPairFreshness, quotesFreshness, rowsFreshness, valuationFreshness } from "../freshness";
|
|
190
193
|
import { fxRateNotes } from "../../plugins/builtin/shared/fx-rate-freshness";
|
|
191
194
|
import { quoteBarNotes } from "../../market-data/quotes/intraday-bar";
|
|
192
|
-
import { depositaryPairText, isReceiptSide, listingDepositaryPair } from "../../market-data/depositary-pair";
|
|
195
|
+
import { cedearRatioText, depositaryPairText, isReceiptSide, listingCedearRatio, listingDepositaryPair } from "../../market-data/depositary-pair";
|
|
193
196
|
import { isPesoDollarPair, loadPesoDollarRates, pesoDollarRateLines, pesoDollarRatesJson } from "../peso-dollar-rates";
|
|
194
197
|
import { atRegularSessionPrice, regularSessionValuationPrice } from "../../market-data/quotes/regular-session-valuation";
|
|
195
198
|
import { viewMarketCapitalization } from "../../utils/market-capitalization";
|
|
@@ -199,6 +202,7 @@ import {
|
|
|
199
202
|
cloudNewsFreshness,
|
|
200
203
|
cloudRealtimeAccess,
|
|
201
204
|
REPORTED_DATA,
|
|
205
|
+
SEC_FILING_ROWS,
|
|
202
206
|
SEC_FILINGS,
|
|
203
207
|
} from "../../plugins/builtin/shared/report-freshness";
|
|
204
208
|
import {
|
|
@@ -242,7 +246,7 @@ import {
|
|
|
242
246
|
type ListingIdentity,
|
|
243
247
|
} from "../listing-arg";
|
|
244
248
|
import { bareListingNote, listingFateNotes, noLongerTradingNote, tradingListing } from "../../tickers/listing-fate";
|
|
245
|
-
import { isYieldIndexSymbol } from "
|
|
249
|
+
import { isYieldIndexSymbol } from "../../tickers/index-roots";
|
|
246
250
|
import {
|
|
247
251
|
analyzeTaxEquivalent, distributionBasis, loadTreasuryTenors, parseTaxRates, TAX_RATE_HELP_OPTIONS, taxEquivalentJson, type TaxRates,
|
|
248
252
|
} from "../../market-data/tax-equivalent";
|
|
@@ -913,7 +917,7 @@ function corporateActionRows(data: CorporateActionsData) {
|
|
|
913
917
|
...declaredDividends(data).map((event) => ({
|
|
914
918
|
type: "dividend",
|
|
915
919
|
date: event.exDate,
|
|
916
|
-
detail: `declared ${currency ? formatDistributionAmount(event.amount, currency) : cleanDecimal(event.amount)}`
|
|
920
|
+
detail: `declared ${event.special ? "special " : ""}${currency ? formatDistributionAmount(event.amount, currency) : cleanDecimal(event.amount)}`
|
|
917
921
|
+ (event.payDate ? `, payable ${event.payDate}` : ""),
|
|
918
922
|
})),
|
|
919
923
|
...data.splits.map((event) => ({
|
|
@@ -1105,8 +1109,10 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
|
|
|
1105
1109
|
const quiet = ended ? null : noPrintsTodayText(row.quote, now);
|
|
1106
1110
|
return [ended ?? (quiet ? `${label}: ${quiet}` : []), quoteBarNotes(label, row.quote, now)].flat();
|
|
1107
1111
|
}),
|
|
1108
|
-
// A receipt and its home line each say the ratio, as `ticker` does.
|
|
1112
|
+
// A receipt and its home line each say the ratio, as `ticker` does; a CEDEAR names itself in its own.
|
|
1109
1113
|
...data.flatMap((row) => {
|
|
1114
|
+
const cedear = listingCedearRatio({ quote: row.quote });
|
|
1115
|
+
if (cedear) return [`${cedearRatioText(cedear)}.`];
|
|
1110
1116
|
const pair = listingDepositaryPair({ quote: row.quote });
|
|
1111
1117
|
return pair ? [`${row.target.symbol}: ${depositaryPairText(pair, isReceiptSide({ quote: row.quote }))}.`] : [];
|
|
1112
1118
|
}),
|
|
@@ -1932,26 +1938,29 @@ const NEWS_COLUMNS = [
|
|
|
1932
1938
|
async function printNewsSearch(
|
|
1933
1939
|
text: string,
|
|
1934
1940
|
articles: NewsArticle[],
|
|
1935
|
-
{ reason, empty, commodity = true, failure, ticker = null }: {
|
|
1941
|
+
{ reason, empty, commodity = true, failure, ticker = null, closest = false }: {
|
|
1936
1942
|
reason?: string;
|
|
1937
1943
|
empty?: string;
|
|
1938
1944
|
commodity?: boolean;
|
|
1939
1945
|
failure?: string;
|
|
1940
1946
|
/** The listing a coin pair's search stands in for. */
|
|
1941
1947
|
ticker?: string | null;
|
|
1948
|
+
/** No story has every word: these are the ones with the most. */
|
|
1949
|
+
closest?: boolean;
|
|
1942
1950
|
},
|
|
1943
1951
|
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
1944
1952
|
) {
|
|
1945
1953
|
const searched = failure === undefined;
|
|
1946
1954
|
const named = searched && commodity && articles.length === 0 && commodityNamedIn(text) !== null;
|
|
1947
1955
|
const route = named ? await commodityNewsRoute(text) : null;
|
|
1956
|
+
const closestReason = closest && articles.length > 0 ? closestStoriesReason(text) : null;
|
|
1948
1957
|
ctx.printResult({
|
|
1949
1958
|
data: articles,
|
|
1950
1959
|
...(searched ? {} : { warnings: [`Searching stories for "${text}" failed: ${failure}`] }),
|
|
1951
|
-
metadata: { ticker, search: text, feed: null, ...(route ? { futuresNews: route.command } : {}) },
|
|
1960
|
+
metadata: { ticker, search: text, feed: null, ...(closestReason ? { match: "closest" } : {}), ...(route ? { futuresNews: route.command } : {}) },
|
|
1952
1961
|
freshness: rowsFreshness(newsRows(articles), cloudNewsFreshness(await cloudRealtimeAccess())),
|
|
1953
1962
|
}, {
|
|
1954
|
-
heading: newsSearchHeading(text, reason),
|
|
1963
|
+
heading: newsSearchHeading(text, [reason, closestReason].filter(Boolean).join(" · ") || undefined),
|
|
1955
1964
|
rows: newsRows,
|
|
1956
1965
|
columns: NEWS_COLUMNS,
|
|
1957
1966
|
empty: named ? noStoriesMessage(text, route) : empty ?? (searched ? noStoriesMessage(text, null) : "No stories to show."),
|
|
@@ -2013,7 +2022,8 @@ async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
2013
2022
|
if (exchangeOption || feed) ctx.fail(`${exchangeOption ? "--exchange" : "--feed"} goes with a symbol, not with text to search stories for.`, "Usage: gloom news <query>");
|
|
2014
2023
|
const problem = newsSearchTextProblem(text);
|
|
2015
2024
|
if (problem) ctx.fail(problem);
|
|
2016
|
-
|
|
2025
|
+
const answer = await searchClosestNewsStories(text, limit);
|
|
2026
|
+
await printNewsSearch(text, answer.found, { closest: answer.closest }, ctx);
|
|
2017
2027
|
return;
|
|
2018
2028
|
}
|
|
2019
2029
|
// A metal's future or name is its market's news, not a company's: GOLD in capitals stays Gold.com's.
|
|
@@ -2193,7 +2203,7 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2193
2203
|
...listingMetadata(resolved), ...(form ? { form } : {}),
|
|
2194
2204
|
...(page ? { announcementsPage: page.url } : {}),
|
|
2195
2205
|
},
|
|
2196
|
-
freshness: rowsFreshness(filingRows(filings),
|
|
2206
|
+
freshness: rowsFreshness(filingRows(filings), SEC_FILING_ROWS),
|
|
2197
2207
|
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
2198
2208
|
}, {
|
|
2199
2209
|
heading: listingHeading(resolved),
|
|
@@ -2205,7 +2215,7 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2205
2215
|
empty: [form ? `No ${form} filings found for ${listingTitle(resolved)}.`
|
|
2206
2216
|
: identity ? noSecReportsNotice(resolved)
|
|
2207
2217
|
: `No SEC filings found for ${listingTitle(resolved)}.`, pageLine].filter(Boolean).join("\n"),
|
|
2208
|
-
...(pageLine
|
|
2218
|
+
...filingsFootnote(pageLine, filings[0]?.companyFrom),
|
|
2209
2219
|
});
|
|
2210
2220
|
} catch (error) {
|
|
2211
2221
|
if (!(error instanceof SecRegistrantMismatchError)) throw error;
|
|
@@ -2223,6 +2233,16 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2223
2233
|
});
|
|
2224
2234
|
}
|
|
2225
2235
|
|
|
2236
|
+
/**
|
|
2237
|
+
* The lines `filings` prints under its rows and CSV writes as notes: an ASX
|
|
2238
|
+
* listing's announcements page, and whose filings a foreign line shows when
|
|
2239
|
+
* they are its company's home line's.
|
|
2240
|
+
*/
|
|
2241
|
+
function filingsFootnote(pageLine: string | null | undefined, companyFrom: CompanyFrom | undefined): { footnote?: string; exportNotes?: string[] } {
|
|
2242
|
+
const lines = [...(pageLine ? [pageLine] : []), ...companyDataNotes(companyFrom)];
|
|
2243
|
+
return lines.length ? { footnote: lines.join("\n"), exportNotes: lines } : {};
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2226
2246
|
/**
|
|
2227
2247
|
* `filings` for a company on ASX that files nothing with the SEC: its latest
|
|
2228
2248
|
* ASX announcements from Gloom Cloud, newest first, under the line saying it
|
|
@@ -2393,7 +2413,8 @@ async function printBeneficialOwners(
|
|
|
2393
2413
|
heading: `${listingHeading(identity)}${cliStyles.muted(beneficialListFacts(payload, form, rows.length, history).map((fact) => ` · ${fact}`).join(""))}`,
|
|
2394
2414
|
// Text draws each column as the HDS report does; CSV and JSON keep the raw values.
|
|
2395
2415
|
textColumns: BENEFICIAL_REPORT_COLUMNS.map((column) => ({ ...column, format: column.textFormat ?? column.format })),
|
|
2396
|
-
|
|
2416
|
+
// A foreign line's owners read from its company's home line say whose.
|
|
2417
|
+
...(legend || payload.companyFrom ? { footnote: [legend, ...companyDataNotes(payload.companyFrom)].filter(Boolean).join("\n") } : {}),
|
|
2397
2418
|
columns: [
|
|
2398
2419
|
...BENEFICIAL_REPORT_COLUMNS.map(({ key, header, align }) => ({ key, header, ...(align ? { align } : {}) })),
|
|
2399
2420
|
{ key: "otherPersons", header: "Other persons" },
|
|
@@ -2494,7 +2515,7 @@ async function runHolders(
|
|
|
2494
2515
|
const columns = holderReportColumns(shownRows, valueCurrency);
|
|
2495
2516
|
// Dated as HDS dates it: by the list's as-of; an insider report of Form 4 filings alone as INS dates it, by the newest.
|
|
2496
2517
|
const freshness = form4Only
|
|
2497
|
-
? rowsFreshness(form4?.newestFiled ? [{ filingDate: form4.newestFiled }] : [], { ...
|
|
2518
|
+
? rowsFreshness(form4?.newestFiled ? [{ filingDate: form4.newestFiled }] : [], { ...SEC_FILING_ROWS, basis: "Form 4 filings" })
|
|
2498
2519
|
: rowsFreshness(shownRows, { ...SEC_FILINGS, basis: "13F filings" }, { asOf: data.asOf ?? null });
|
|
2499
2520
|
// Named as fn HDS takes it, so the closing notes point to the same listing.
|
|
2500
2521
|
const requested = exchangeOption && !raw.includes(":") ? `${raw}:${exchangeOption.toUpperCase()}` : raw;
|
|
@@ -2564,6 +2585,8 @@ async function runHolders(
|
|
|
2564
2585
|
insiderCommand ? insiderSummary(data, listing, identity, form4) : "",
|
|
2565
2586
|
].filter(Boolean).join("\n"),
|
|
2566
2587
|
empty: `No holders reported for ${listingTitle(identity)}.`,
|
|
2588
|
+
// Holders read from the company's home line say whose; the SEC index's are the listing's own.
|
|
2589
|
+
...(!indexed && data.companyFrom ? { footnote: companyDataNotes(data.companyFrom).join("\n") } : {}),
|
|
2567
2590
|
});
|
|
2568
2591
|
});
|
|
2569
2592
|
}
|
|
@@ -82,6 +82,28 @@ export async function searchNewsStories(text: string, limit: number, topics?: re
|
|
|
82
82
|
return response.items.map((item) => mapCloudNewsArticle(item));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/** A search's stories, and whether they are its closest ones because no story has every word. */
|
|
86
|
+
export interface NewsSearchAnswer {
|
|
87
|
+
found: NewsArticle[];
|
|
88
|
+
closest: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The stories matching the text; when no story has every word of a search of
|
|
93
|
+
* several, the ones with the most of them, most words first. Spellings of
|
|
94
|
+
* one word are one word to the service: lockup, lock-up and lock up;
|
|
95
|
+
* spinoff and spin-off; expiry and expiration.
|
|
96
|
+
*/
|
|
97
|
+
export async function searchClosestNewsStories(text: string, limit: number): Promise<NewsSearchAnswer> {
|
|
98
|
+
const response = await apiClient.searchCloudNews(text, limit, undefined, { closest: true });
|
|
99
|
+
return { found: response.items.map((item) => mapCloudNewsArticle(item)), closest: response.match === "closest" };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** What the heading says of a search no story has every word of. */
|
|
103
|
+
export function closestStoriesReason(text: string): string {
|
|
104
|
+
return `no story has every word of "${text}"; the closest, most words first`;
|
|
105
|
+
}
|
|
106
|
+
|
|
85
107
|
/** The NI code of the metal markets: prices, futures, bullion and the trusts that hold it, not a miner's releases. */
|
|
86
108
|
export const METALS_NEWS_TOPIC = "metals";
|
|
87
109
|
|