@bloomai/cli 0.1.4 → 0.1.6
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 +76 -22
- package/dist/index.js +756 -99
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -34,37 +34,70 @@ jq '.data[] | select(.revenue_growth > 0.1)' /tmp/bloom/aapl-revenue.json
|
|
|
34
34
|
|
|
35
35
|
## Commands
|
|
36
36
|
|
|
37
|
+
### Core Research
|
|
37
38
|
| Command | What it does | Example |
|
|
38
39
|
|---------|-------------|---------|
|
|
39
40
|
| `info` | Company fundamentals, ratings, peers | `bloom info AAPL MSFT` |
|
|
40
41
|
| `news` | Recent news headlines | `bloom news AAPL --limit 5` |
|
|
41
42
|
| `price` | Historical price data | `bloom price AAPL --timeframes 1d,1m,1y` |
|
|
42
43
|
| `financials` | Revenue, margins, cash flow | `bloom financials AAPL --type revenue` |
|
|
43
|
-
| `sentiment` | Market mood (AAII, Fear&Greed, VIX) | `bloom sentiment` |
|
|
44
|
-
| `market` | Market map, sectors, movers | `bloom market --type top_movers` |
|
|
45
|
-
| `collections` | Curated stock lists by theme | `bloom collections --section VALUE` |
|
|
46
|
-
| `screen` | Filter stocks by metrics | `bloom screen "market_cap > 100B" "pe_ratio < 25"` |
|
|
47
44
|
| `earnings` | Earnings history & estimates | `bloom earnings AAPL` |
|
|
45
|
+
| `earnings-calendar` | Upcoming earnings dates | `bloom earnings-calendar --days 7` |
|
|
48
46
|
| `technicals` | RSI, MAs, patterns, options flow | `bloom technicals AAPL` |
|
|
49
47
|
| `ma` | Raw moving average data (SMA/EMA) | `bloom ma AAPL --sma 50,100,200 --ema 20` |
|
|
50
|
-
| `options-history` | Options sentiment over time | `bloom options-history AAPL
|
|
48
|
+
| `options-history` | Options sentiment evolution over time | `bloom options-history AAPL` |
|
|
49
|
+
| `transcript` | Search earnings call transcripts | `bloom transcript AAPL "AI" "services"` |
|
|
50
|
+
| `compare` | Side-by-side stock comparison | `bloom compare AAPL MSFT GOOG` |
|
|
51
|
+
|
|
52
|
+
### Valuation & Risk
|
|
53
|
+
| Command | What it does | Example |
|
|
54
|
+
|---------|-------------|---------|
|
|
55
|
+
| `dcf` | Discounted cash flow valuation | `bloom dcf AAPL` |
|
|
56
|
+
| `risk` | Risk factors from SEC 10-K filings | `bloom risk AAPL` |
|
|
57
|
+
| `ratings` | Analyst grades & price targets | `bloom ratings AAPL` |
|
|
58
|
+
| `short-interest` | Short interest history | `bloom short-interest AAPL` |
|
|
59
|
+
| `holders` | Institutional & mutual fund holders | `bloom holders AAPL` |
|
|
60
|
+
| `insider` | SEC Form 4 insider trading | `bloom insider AAPL` |
|
|
61
|
+
|
|
62
|
+
### Portfolio & Strategy
|
|
63
|
+
| Command | What it does | Example |
|
|
64
|
+
|---------|-------------|---------|
|
|
51
65
|
| `portfolio` | Portfolio risk analysis | `bloom portfolio '{"AAPL":0.5,"MSFT":0.5}'` |
|
|
66
|
+
| `backtest` | Portfolio backtest against benchmark | `bloom backtest --symbols AAPL,MSFT --allocations 0.5,0.5` |
|
|
67
|
+
| `correlation` | Correlation matrix for symbols | `bloom correlation AAPL MSFT GOOG` |
|
|
68
|
+
| `drawdown` | Portfolio drawdown analysis | `bloom drawdown --symbols AAPL,MSFT --allocations 0.5,0.5` |
|
|
69
|
+
| `position-size` | Kelly criterion position sizing | `bloom position-size --bull 0.6 --bear -0.15 --conviction 8 --value 100000` |
|
|
52
70
|
| `trades` | AI trade evaluation | `bloom trades "Buy AAPL: strong AI catalysts"` |
|
|
53
71
|
| `catalysts` | Forward-looking value drivers | `bloom catalysts AAPL MSFT` |
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
72
|
+
|
|
73
|
+
### Market & Macro
|
|
74
|
+
| Command | What it does | Example |
|
|
75
|
+
|---------|-------------|---------|
|
|
76
|
+
| `sentiment` | Market mood (AAII, Fear&Greed, VIX) | `bloom sentiment` |
|
|
77
|
+
| `market` | Market map, sectors, movers | `bloom market --type top_movers` |
|
|
78
|
+
| `macro` | GDP, CPI, unemployment, fed funds, treasury yields | `bloom macro --indicators GDP,CPI` |
|
|
79
|
+
| `sectors` | Sector performance across time periods | `bloom sectors` |
|
|
80
|
+
| `screen` | Filter stocks by metrics | `bloom screen "market_cap > 100B" "pe_ratio < 25"` |
|
|
81
|
+
| `collections` | Curated stock lists by theme | `bloom collections --section VALUE` |
|
|
82
|
+
|
|
83
|
+
### Corporate Events
|
|
84
|
+
| Command | What it does | Example |
|
|
85
|
+
|---------|-------------|---------|
|
|
86
|
+
| `dividends` | Dividend history | `bloom dividends AAPL` |
|
|
87
|
+
| `splits` | Stock split history | `bloom splits AAPL` |
|
|
88
|
+
| `ipo` | Upcoming and recent IPOs | `bloom ipo --from 2026-05-01` |
|
|
89
|
+
|
|
90
|
+
### AI Arena
|
|
91
|
+
| Command | What it does | Example |
|
|
92
|
+
|---------|-------------|---------|
|
|
93
|
+
| `ai-trades` | Recent AI model trade decisions | `bloom ai-trades --model claude --limit 5` |
|
|
57
94
|
| `ai-portfolio` | AI model portfolio positions & performance | `bloom ai-portfolio --model openai` |
|
|
58
|
-
| `search` | Web search via Serper (Google) | `bloom search "nvidia earnings Q1 2026"` |
|
|
59
|
-
| `scrape` | Scrape a web page via Firecrawl | `bloom scrape https://example.com` |
|
|
60
95
|
|
|
61
96
|
## Environment Variables
|
|
62
97
|
|
|
63
98
|
| Variable | Required for | Description |
|
|
64
99
|
|----------|-------------|-------------|
|
|
65
|
-
| `BLOOM_API_KEY` | All
|
|
66
|
-
| `SERPER_API_KEY` | `bloom search` | Serper.dev API key for web search |
|
|
67
|
-
| `FIRECRAWL_API_KEY` | `bloom scrape` | Firecrawl API key for web scraping |
|
|
100
|
+
| `BLOOM_API_KEY` | All commands | Bloom API key (`bloom auth login`) |
|
|
68
101
|
|
|
69
102
|
## Global Flags
|
|
70
103
|
|
|
@@ -164,23 +197,44 @@ bloom ai-portfolio --model gemini
|
|
|
164
197
|
jq '[.[] | {model: .ai_model, return_pct, account_value}]' /tmp/bloom/ai-portfolio.json
|
|
165
198
|
```
|
|
166
199
|
|
|
167
|
-
###
|
|
200
|
+
### Valuation & Risk Research
|
|
201
|
+
```bash
|
|
202
|
+
# DCF valuation
|
|
203
|
+
bloom dcf AAPL -o /tmp/bloom/aapl-dcf.json
|
|
204
|
+
|
|
205
|
+
# Risk factors from 10-K filing
|
|
206
|
+
bloom risk AAPL -o /tmp/bloom/aapl-risks.json
|
|
207
|
+
|
|
208
|
+
# Analyst ratings and price targets
|
|
209
|
+
bloom ratings AAPL
|
|
210
|
+
|
|
211
|
+
# Who owns this stock?
|
|
212
|
+
bloom holders AAPL --limit 10
|
|
213
|
+
bloom insider AAPL
|
|
214
|
+
|
|
215
|
+
# Short interest
|
|
216
|
+
bloom short-interest AAPL
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Portfolio Analytics
|
|
168
220
|
```bash
|
|
169
|
-
#
|
|
170
|
-
bloom
|
|
221
|
+
# Backtest a portfolio
|
|
222
|
+
bloom backtest --symbols AAPL,MSFT,GOOG --allocations 0.4,0.3,0.3 -o /tmp/bloom/backtest.json
|
|
171
223
|
|
|
172
|
-
#
|
|
173
|
-
bloom
|
|
224
|
+
# Correlation matrix
|
|
225
|
+
bloom correlation AAPL MSFT GOOG AMZN
|
|
174
226
|
|
|
175
|
-
#
|
|
176
|
-
bloom
|
|
227
|
+
# Drawdown analysis
|
|
228
|
+
bloom drawdown --symbols AAPL,MSFT --allocations 0.5,0.5
|
|
177
229
|
|
|
178
|
-
#
|
|
179
|
-
bloom
|
|
230
|
+
# Macro overview
|
|
231
|
+
bloom macro --indicators GDP,CPI,UNEMPLOYMENT
|
|
232
|
+
bloom sectors
|
|
180
233
|
```
|
|
181
234
|
|
|
182
235
|
## Tips
|
|
183
236
|
|
|
237
|
+
- **Every command has `--help` with examples.** Run `bloom <command> --help` to see usage patterns, available options, and practical examples.
|
|
184
238
|
- Use `-o` to download data to files, then `jq` to extract. Saves context tokens.
|
|
185
239
|
- Batch multiple tickers: `bloom info AAPL MSFT GOOG` (up to 10).
|
|
186
240
|
- Use `bloom screen` first to narrow your universe, then deep-dive with other commands.
|
package/dist/index.js
CHANGED
|
@@ -110,7 +110,8 @@ function registerAuthCommand(program2) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// src/api.ts
|
|
113
|
-
var
|
|
113
|
+
var API_BASE = "https://api.getbloom.app";
|
|
114
|
+
var MCP_URL = `${API_BASE}/mcp/`;
|
|
114
115
|
var requestId = 0;
|
|
115
116
|
async function callTool(toolName, args) {
|
|
116
117
|
const apiKey = requireApiKey();
|
|
@@ -150,6 +151,27 @@ async function callTool(toolName, args) {
|
|
|
150
151
|
return text;
|
|
151
152
|
}
|
|
152
153
|
}
|
|
154
|
+
async function callRest(path2, params) {
|
|
155
|
+
const apiKey = requireApiKey();
|
|
156
|
+
const url = new URL(path2, API_BASE);
|
|
157
|
+
if (params) {
|
|
158
|
+
for (const [k, v] of Object.entries(params)) {
|
|
159
|
+
url.searchParams.set(k, v);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const res = await fetch(url.toString(), {
|
|
163
|
+
method: "GET",
|
|
164
|
+
headers: {
|
|
165
|
+
Authorization: `Bearer ${apiKey}`,
|
|
166
|
+
"Content-Type": "application/json"
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
if (!res.ok) {
|
|
170
|
+
const text = await res.text().catch(() => "");
|
|
171
|
+
throw new Error(`API error ${res.status}: ${text || res.statusText}`);
|
|
172
|
+
}
|
|
173
|
+
return res.json();
|
|
174
|
+
}
|
|
153
175
|
|
|
154
176
|
// src/format.ts
|
|
155
177
|
import Table from "cli-table3";
|
|
@@ -165,12 +187,12 @@ function outputResult(data, opts, tableFormatter) {
|
|
|
165
187
|
emit(JSON.stringify(data, null, 2), opts);
|
|
166
188
|
return;
|
|
167
189
|
}
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
190
|
+
const fmt2 = resolveFormat(opts);
|
|
191
|
+
if (fmt2 === "json") {
|
|
170
192
|
emit(JSON.stringify(data, null, 2), opts);
|
|
171
193
|
return;
|
|
172
194
|
}
|
|
173
|
-
if (
|
|
195
|
+
if (fmt2 === "csv") {
|
|
174
196
|
emit(toCSV(data), opts);
|
|
175
197
|
return;
|
|
176
198
|
}
|
|
@@ -354,7 +376,12 @@ function formatSection(obj, indent = "") {
|
|
|
354
376
|
function registerNewsCommand(program2) {
|
|
355
377
|
addGlobalOptions(
|
|
356
378
|
program2.command("news").description("Recent news from Benzinga channels").argument("<symbols...>", "Ticker symbols").option("--limit <n>", "Max articles (default: 15)", "15")
|
|
357
|
-
).
|
|
379
|
+
).addHelpText("after", `
|
|
380
|
+
Examples:
|
|
381
|
+
$ bloom news AAPL Latest Apple news
|
|
382
|
+
$ bloom news AAPL MSFT GOOGL News for multiple stocks
|
|
383
|
+
$ bloom news TSLA --limit 5 Last 5 Tesla articles
|
|
384
|
+
`).action(async (symbols, opts) => {
|
|
358
385
|
const data = await callTool("get_news_for_symbols", {
|
|
359
386
|
symbols: symbols.map((s) => s.toUpperCase()),
|
|
360
387
|
limit: parseIntSafe(opts.limit, 15)
|
|
@@ -390,7 +417,13 @@ function formatNews(data, opts) {
|
|
|
390
417
|
function registerPriceCommand(program2) {
|
|
391
418
|
addGlobalOptions(
|
|
392
419
|
program2.command("price").description("Historical price data").argument("<symbol>", "Ticker symbol").option("--timeframes <tf>", "Comma-separated: 1d,1w,1m,3m,6m,ytd,1y,2y,5y", "1d,1m,1y").option("--ohlc", "Include full OHLC data")
|
|
393
|
-
).
|
|
420
|
+
).addHelpText("after", `
|
|
421
|
+
Examples:
|
|
422
|
+
$ bloom price AAPL Default timeframes (1d, 1m, 1y)
|
|
423
|
+
$ bloom price TSLA --timeframes 1d,5y Daily and 5-year price history
|
|
424
|
+
$ bloom price MSFT --ohlc Include open/high/low/close
|
|
425
|
+
$ bloom price AAPL -f csv -o prices.csv Export to CSV file
|
|
426
|
+
`).action(async (symbol, opts) => {
|
|
394
427
|
const data = await callTool("get_price_charts", {
|
|
395
428
|
symbol: symbol.toUpperCase(),
|
|
396
429
|
timeframes: opts.timeframes.split(",").map((t) => t.trim()),
|
|
@@ -459,7 +492,13 @@ var CHART_TYPES = [
|
|
|
459
492
|
function registerFinancialsCommand(program2) {
|
|
460
493
|
addGlobalOptions(
|
|
461
494
|
program2.command("financials").description("Quarterly/annual financial data with YoY growth").argument("<symbol>", "Ticker symbol").option("--type <type>", "Chart type: " + CHART_TYPES.join(", "), "revenue")
|
|
462
|
-
).
|
|
495
|
+
).addHelpText("after", `
|
|
496
|
+
Examples:
|
|
497
|
+
$ bloom financials AAPL Revenue (default)
|
|
498
|
+
$ bloom financials TSLA --type operating_margin Operating margin trend
|
|
499
|
+
$ bloom financials MSFT --type free_cash_flow Free cash flow history
|
|
500
|
+
$ bloom financials NVDA --type net_earnings_growth Earnings growth rate
|
|
501
|
+
`).action(async (symbol, opts) => {
|
|
463
502
|
const data = await callTool("get_financial_charts", {
|
|
464
503
|
symbol: symbol.toUpperCase(),
|
|
465
504
|
chart_type: opts.type
|
|
@@ -478,7 +517,11 @@ var SECTIONS = [
|
|
|
478
517
|
function registerSentimentCommand(program2) {
|
|
479
518
|
addGlobalOptions(
|
|
480
519
|
program2.command("sentiment").description("Market mood: AAII, Fear&Greed, VIX, momentum").option("--sections <s>", "Comma-separated: " + SECTIONS.join(","))
|
|
481
|
-
).
|
|
520
|
+
).addHelpText("after", `
|
|
521
|
+
Examples:
|
|
522
|
+
$ bloom sentiment All sentiment indicators
|
|
523
|
+
$ bloom sentiment --sections aaii_sentiment,cnn_fear_greed Just AAII and Fear & Greed
|
|
524
|
+
`).action(async (opts) => {
|
|
482
525
|
const args = {};
|
|
483
526
|
if (opts.sections) {
|
|
484
527
|
args.sections = opts.sections.split(",").map((s) => s.trim());
|
|
@@ -514,7 +557,14 @@ function formatSentiment(data) {
|
|
|
514
557
|
function registerMarketCommand(program2) {
|
|
515
558
|
addGlobalOptions(
|
|
516
559
|
program2.command("market").description("Market performance, sectors, global").option("--type <type>", "top_movers|sector_breakdown|global_markets|major_indexes", "top_movers").option("--period <p>", "1d|1w|1m|3m|6m|ytd|1y|5y", "1d").option("--limit <n>", "Max stocks for top_movers", "24").option("--universe <u>", "all|sp500|international|crypto|community", "all")
|
|
517
|
-
).
|
|
560
|
+
).addHelpText("after", `
|
|
561
|
+
Examples:
|
|
562
|
+
$ bloom market Today's top movers
|
|
563
|
+
$ bloom market --type sector_breakdown Sector performance
|
|
564
|
+
$ bloom market --type global_markets Global market overview
|
|
565
|
+
$ bloom market --type top_movers --period 1w Weekly top movers
|
|
566
|
+
$ bloom market --universe sp500 --limit 10 Top 10 S&P 500 movers
|
|
567
|
+
`).action(async (opts) => {
|
|
518
568
|
const data = await callTool("get_market_map", {
|
|
519
569
|
map_type: opts.type,
|
|
520
570
|
period: opts.period,
|
|
@@ -529,7 +579,13 @@ function registerMarketCommand(program2) {
|
|
|
529
579
|
function registerCollectionsCommand(program2) {
|
|
530
580
|
addGlobalOptions(
|
|
531
581
|
program2.command("collections").description("Curated stock collections by theme").option("--section <s>", "GET_STARTED|VALUE|TRENDS|BEAT_UP|FAMOUS_INVESTORS|HIGH_FLYERS|SECTORS|GEO|LEADERBOARDS", "GET_STARTED").option("--limit <n>", "Max collections", "6")
|
|
532
|
-
).
|
|
582
|
+
).addHelpText("after", `
|
|
583
|
+
Examples:
|
|
584
|
+
$ bloom collections Default collections
|
|
585
|
+
$ bloom collections --section VALUE Value investing picks
|
|
586
|
+
$ bloom collections --section FAMOUS_INVESTORS Famous investor portfolios
|
|
587
|
+
$ bloom collections --section TRENDS --limit 3 Top 3 trending collections
|
|
588
|
+
`).action(async (opts) => {
|
|
533
589
|
const data = await callTool("get_collections", {
|
|
534
590
|
section: opts.section,
|
|
535
591
|
limit: parseIntSafe(opts.limit, 6)
|
|
@@ -596,7 +652,11 @@ function formatScreen(data, opts) {
|
|
|
596
652
|
function registerEarningsCommand(program2) {
|
|
597
653
|
addGlobalOptions(
|
|
598
654
|
program2.command("earnings").description("Earnings history, estimates, surprises").argument("<symbols...>", "Ticker symbols (up to 5)")
|
|
599
|
-
).
|
|
655
|
+
).addHelpText("after", `
|
|
656
|
+
Examples:
|
|
657
|
+
$ bloom earnings AAPL Apple earnings history and surprises
|
|
658
|
+
$ bloom earnings AAPL MSFT GOOGL Compare earnings for multiple stocks
|
|
659
|
+
`).action(async (symbols, opts) => {
|
|
600
660
|
const data = await callTool("analyze_earnings", {
|
|
601
661
|
symbols: symbols.map((s) => s.toUpperCase()).slice(0, 5)
|
|
602
662
|
});
|
|
@@ -604,12 +664,60 @@ function registerEarningsCommand(program2) {
|
|
|
604
664
|
});
|
|
605
665
|
}
|
|
606
666
|
|
|
667
|
+
// src/commands/earnings-calendar.ts
|
|
668
|
+
function registerEarningsCalendarCommand(program2) {
|
|
669
|
+
addGlobalOptions(
|
|
670
|
+
program2.command("earnings-calendar").description(
|
|
671
|
+
"Upcoming earnings dates (all companies, or filter by symbols)"
|
|
672
|
+
).argument("[symbols...]", "Ticker symbols (omit to show all)").option("--days <n>", "Look-ahead window in days (1-90)", "28")
|
|
673
|
+
).addHelpText("after", `
|
|
674
|
+
Examples:
|
|
675
|
+
$ bloom earnings-calendar AAPL MSFT GOOGL Upcoming earnings for these stocks
|
|
676
|
+
$ bloom earnings-calendar TSLA --days 7 Earnings within the next week
|
|
677
|
+
$ bloom earnings-calendar NVDA --days 90 Earnings within 90 days
|
|
678
|
+
`).action(
|
|
679
|
+
async (symbols, opts) => {
|
|
680
|
+
const days = parseIntSafe(opts.days, 28);
|
|
681
|
+
const params = { days: String(days) };
|
|
682
|
+
if (symbols.length > 0) {
|
|
683
|
+
params.symbols = symbols.map((s) => s.toUpperCase()).join(",");
|
|
684
|
+
}
|
|
685
|
+
const data = await callRest(
|
|
686
|
+
"/markets/earnings-calendar/",
|
|
687
|
+
params
|
|
688
|
+
);
|
|
689
|
+
const items = data.results ?? [];
|
|
690
|
+
outputResult(items, opts, (d, o) => {
|
|
691
|
+
const rows = d;
|
|
692
|
+
if (rows.length === 0) return "(no upcoming earnings)";
|
|
693
|
+
return makeTable(
|
|
694
|
+
["Date", "Symbol", "Name", "Est. EPS", "Actual EPS"],
|
|
695
|
+
rows.map((r) => [
|
|
696
|
+
r.earnings_date ?? "\u2014",
|
|
697
|
+
r.symbol,
|
|
698
|
+
r.name,
|
|
699
|
+
r.eps_estimate != null ? `$${r.eps_estimate}` : "\u2014",
|
|
700
|
+
r.eps_actual != null ? `$${r.eps_actual}` : "\u2014"
|
|
701
|
+
]),
|
|
702
|
+
o
|
|
703
|
+
);
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
|
|
607
709
|
// src/commands/technicals.ts
|
|
608
710
|
var SECTIONS2 = ["technical_indicators", "options_analysis", "insider_activity"];
|
|
609
711
|
function registerTechnicalsCommand(program2) {
|
|
610
712
|
addGlobalOptions(
|
|
611
713
|
program2.command("technicals").description("RSI, MAs, patterns, options flow, insider trades").argument("<symbols...>", "Ticker symbols (up to 5)").option("--sections <s>", "Comma-separated: " + SECTIONS2.join(","))
|
|
612
|
-
).
|
|
714
|
+
).addHelpText("after", `
|
|
715
|
+
Examples:
|
|
716
|
+
$ bloom technicals AAPL All technical data
|
|
717
|
+
$ bloom technicals TSLA --sections technical_indicators RSI, MAs, patterns only
|
|
718
|
+
$ bloom technicals NVDA --sections options_analysis Options flow only
|
|
719
|
+
$ bloom technicals AAPL --sections insider_activity Insider trades only
|
|
720
|
+
`).action(async (symbols, opts) => {
|
|
613
721
|
const args = {
|
|
614
722
|
symbols: symbols.map((s) => s.toUpperCase()).slice(0, 5)
|
|
615
723
|
};
|
|
@@ -625,7 +733,13 @@ function registerTechnicalsCommand(program2) {
|
|
|
625
733
|
function registerOptionsCommand(program2) {
|
|
626
734
|
addGlobalOptions(
|
|
627
735
|
program2.command("options-history").description("Options sentiment evolution over time").argument("<symbol>", "Ticker symbol").option("--days <n>", "Lookback days (7-90)", "30").option("--weekly", "Use weekly sampling (default)").option("--daily", "Use daily data").option("--greeks", "Include advanced Greeks analysis")
|
|
628
|
-
).
|
|
736
|
+
).addHelpText("after", `
|
|
737
|
+
Examples:
|
|
738
|
+
$ bloom options-history AAPL 30-day options sentiment (default)
|
|
739
|
+
$ bloom options-history TSLA --days 90 90-day lookback
|
|
740
|
+
$ bloom options-history NVDA --daily Daily granularity
|
|
741
|
+
$ bloom options-history AAPL --greeks Include advanced Greeks analysis
|
|
742
|
+
`).action(
|
|
629
743
|
async (symbol, opts) => {
|
|
630
744
|
const data = await callTool("analyze_options_history", {
|
|
631
745
|
symbol: symbol.toUpperCase(),
|
|
@@ -661,7 +775,11 @@ function registerPortfolioCommand(program2) {
|
|
|
661
775
|
function registerTradesCommand(program2) {
|
|
662
776
|
addGlobalOptions(
|
|
663
777
|
program2.command("trades").description("AI trade evaluation").argument("<trades...>", 'Trade descriptions: "Buy AAPL 30% because..." "Sell MSFT 20% because..."')
|
|
664
|
-
).
|
|
778
|
+
).addHelpText("after", `
|
|
779
|
+
Examples:
|
|
780
|
+
$ bloom trades "Buy AAPL 30% because of strong iPhone sales"
|
|
781
|
+
$ bloom trades "Sell MSFT 20% to take profits" "Buy GOOGL 15% on AI momentum"
|
|
782
|
+
`).action(async (tradeStrs, opts) => {
|
|
665
783
|
const trades = tradeStrs.map(parseTrade);
|
|
666
784
|
const data = await callTool("analyze_trades", { trades });
|
|
667
785
|
outputResult(data, opts);
|
|
@@ -694,7 +812,11 @@ Format: "Buy AAPL 30% because thesis" or JSON object`
|
|
|
694
812
|
function registerCatalystsCommand(program2) {
|
|
695
813
|
addGlobalOptions(
|
|
696
814
|
program2.command("catalysts").description("Forward-looking value catalysts").argument("<symbols...>", "Ticker symbols (up to 5)")
|
|
697
|
-
).
|
|
815
|
+
).addHelpText("after", `
|
|
816
|
+
Examples:
|
|
817
|
+
$ bloom catalysts AAPL Upcoming catalysts for Apple
|
|
818
|
+
$ bloom catalysts TSLA NVDA AMZN Catalysts for multiple stocks
|
|
819
|
+
`).action(async (symbols, opts) => {
|
|
698
820
|
const data = await callTool("generate_catalysts", {
|
|
699
821
|
symbols: symbols.map((s) => s.toUpperCase()).slice(0, 5)
|
|
700
822
|
});
|
|
@@ -840,7 +962,12 @@ function truncate2(s, max) {
|
|
|
840
962
|
function registerAiPortfolioCommand(program2) {
|
|
841
963
|
addGlobalOptions(
|
|
842
964
|
program2.command("ai-portfolio").description("Current portfolio positions held by AI models (InvestingArena live accounts)").option("--model <m>", "Filter by AI model: claude|openai|gemini")
|
|
843
|
-
).
|
|
965
|
+
).addHelpText("after", `
|
|
966
|
+
Examples:
|
|
967
|
+
$ bloom ai-portfolio All AI model portfolios
|
|
968
|
+
$ bloom ai-portfolio --model claude Claude's current positions
|
|
969
|
+
$ bloom ai-portfolio --model openai OpenAI's current positions
|
|
970
|
+
`).action(async (opts) => {
|
|
844
971
|
const args = {};
|
|
845
972
|
if (opts.model) args.ai_model = opts.model;
|
|
846
973
|
const data = await callTool("get_ai_trade_ideas", args);
|
|
@@ -1010,111 +1137,627 @@ function registerMaCommand(program2) {
|
|
|
1010
1137
|
);
|
|
1011
1138
|
}
|
|
1012
1139
|
|
|
1013
|
-
// src/commands/
|
|
1014
|
-
function
|
|
1140
|
+
// src/commands/backtest.ts
|
|
1141
|
+
function registerBacktestCommand(program2) {
|
|
1015
1142
|
addGlobalOptions(
|
|
1016
|
-
program2.command("
|
|
1017
|
-
).action(async (
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
const q = query.join(" ");
|
|
1024
|
-
const limit = parseIntSafe(opts.limit, 10);
|
|
1025
|
-
const searchType = opts.type || "web";
|
|
1026
|
-
const validTypes = /* @__PURE__ */ new Set(["web", "news", "images"]);
|
|
1027
|
-
if (!validTypes.has(searchType)) {
|
|
1028
|
-
console.error(`Error: Invalid search type "${searchType}". Must be one of: web, news, images`);
|
|
1029
|
-
process.exit(1);
|
|
1030
|
-
}
|
|
1031
|
-
const endpoint = searchType === "web" ? "search" : searchType;
|
|
1032
|
-
const response = await fetch(`https://google.serper.dev/${endpoint}`, {
|
|
1033
|
-
method: "POST",
|
|
1034
|
-
headers: {
|
|
1035
|
-
"X-API-KEY": apiKey,
|
|
1036
|
-
"Content-Type": "application/json"
|
|
1037
|
-
},
|
|
1038
|
-
body: JSON.stringify({ q, num: limit })
|
|
1143
|
+
program2.command("backtest").description("Backtest a portfolio against a benchmark").requiredOption("--symbols <s>", "Comma-separated symbols (e.g., AAPL,MSFT)").requiredOption("--allocations <a>", "Comma-separated weights (e.g., 0.5,0.5)").option("--benchmark <b>", "Benchmark symbol (default: SPY)", "SPY").option("--strategy <s>", "Strategy: buy_and_hold", "buy_and_hold")
|
|
1144
|
+
).action(async (opts) => {
|
|
1145
|
+
const data = await callRest("backtest/", {
|
|
1146
|
+
symbols: opts.symbols,
|
|
1147
|
+
allocations: opts.allocations,
|
|
1148
|
+
benchmark: opts.benchmark,
|
|
1149
|
+
strategy: opts.strategy
|
|
1039
1150
|
});
|
|
1040
|
-
|
|
1041
|
-
const text = await response.text();
|
|
1042
|
-
console.error(`Serper API error (${response.status}): ${text}`);
|
|
1043
|
-
process.exit(1);
|
|
1044
|
-
}
|
|
1045
|
-
const data = await response.json();
|
|
1046
|
-
outputResult(data, opts, (d, o) => formatSearch(d, o));
|
|
1151
|
+
outputResult(data, opts, formatBacktest);
|
|
1047
1152
|
});
|
|
1048
1153
|
}
|
|
1049
|
-
function
|
|
1050
|
-
if (!data || typeof data !== "object") return String(data);
|
|
1154
|
+
function formatBacktest(data) {
|
|
1051
1155
|
const obj = data;
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const rows =
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1156
|
+
if (!obj || typeof obj !== "object") return JSON.stringify(data, null, 2);
|
|
1157
|
+
const intervals = obj.interval_results ?? obj;
|
|
1158
|
+
const rows = [];
|
|
1159
|
+
for (const [interval, stats] of Object.entries(intervals)) {
|
|
1160
|
+
if (typeof stats !== "object" || !stats) continue;
|
|
1161
|
+
const s = stats;
|
|
1162
|
+
rows.push([
|
|
1163
|
+
interval,
|
|
1164
|
+
fmt(s.total_return),
|
|
1165
|
+
fmt(s.benchmark_return),
|
|
1166
|
+
fmt(s.alpha ?? s.excess_return),
|
|
1167
|
+
fmtNum(s.sharpe),
|
|
1168
|
+
fmt(s.max_drawdown)
|
|
1169
|
+
]);
|
|
1170
|
+
}
|
|
1171
|
+
if (rows.length === 0) return JSON.stringify(data, null, 2);
|
|
1172
|
+
return makeTable(["Interval", "Return", "Benchmark", "Alpha", "Sharpe", "Max DD"], rows);
|
|
1173
|
+
}
|
|
1174
|
+
function fmt(v) {
|
|
1175
|
+
if (v == null || v === "") return "-";
|
|
1176
|
+
const n = Number(v);
|
|
1177
|
+
if (isNaN(n)) return String(v);
|
|
1178
|
+
return `${(n * 100).toFixed(2)}%`;
|
|
1179
|
+
}
|
|
1180
|
+
function fmtNum(v) {
|
|
1181
|
+
if (v == null || v === "") return "-";
|
|
1182
|
+
const n = Number(v);
|
|
1183
|
+
if (isNaN(n)) return String(v);
|
|
1184
|
+
return n.toFixed(3);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// src/commands/correlation.ts
|
|
1188
|
+
function registerCorrelationCommand(program2) {
|
|
1189
|
+
addGlobalOptions(
|
|
1190
|
+
program2.command("correlation").description("Correlation matrix for a set of symbols").argument("<symbols...>", "Ticker symbols (2+)").option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)")
|
|
1191
|
+
).addHelpText("after", `
|
|
1192
|
+
Examples:
|
|
1193
|
+
$ bloom correlation AAPL MSFT GOOGL Correlation matrix
|
|
1194
|
+
$ bloom correlation AAPL TSLA --start 2025-01-01 Since Jan 2025
|
|
1195
|
+
$ bloom correlation SPY QQQ IWM --start 2024-01-01 --end 2024-12-31 Full year
|
|
1196
|
+
`).action(async (symbols, opts) => {
|
|
1197
|
+
const params = {
|
|
1198
|
+
symbols: symbols.map((s) => s.toUpperCase()).join(",")
|
|
1199
|
+
};
|
|
1200
|
+
if (opts.start) params.start_date = opts.start;
|
|
1201
|
+
if (opts.end) params.end_date = opts.end;
|
|
1202
|
+
const data = await callRest("correlation/", params);
|
|
1203
|
+
outputResult(data, opts, formatCorrelation);
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
function formatCorrelation(data) {
|
|
1207
|
+
const obj = data;
|
|
1208
|
+
const matrix = obj?.correlation;
|
|
1209
|
+
if (!matrix) return JSON.stringify(data, null, 2);
|
|
1210
|
+
const symbols = Object.keys(matrix);
|
|
1211
|
+
const rows = symbols.map((s) => [s, ...symbols.map((s2) => {
|
|
1212
|
+
const v = matrix[s]?.[s2];
|
|
1213
|
+
return v != null ? v.toFixed(3) : "-";
|
|
1214
|
+
})]);
|
|
1215
|
+
return makeTable(["", ...symbols], rows);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// src/commands/risk.ts
|
|
1219
|
+
function registerRiskCommand(program2) {
|
|
1220
|
+
addGlobalOptions(
|
|
1221
|
+
program2.command("risk").description("Risk factors for a stock").argument("<symbol>", "Ticker symbol")
|
|
1222
|
+
).addHelpText("after", `
|
|
1223
|
+
Examples:
|
|
1224
|
+
$ bloom risk AAPL Risk factors for Apple
|
|
1225
|
+
$ bloom risk TSLA Risk factors for Tesla
|
|
1226
|
+
`).action(async (symbol, opts) => {
|
|
1227
|
+
const data = await callRest(`investments/${symbol.toUpperCase()}/risk_factors/`);
|
|
1228
|
+
outputResult(data, opts, formatRisk);
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
function formatRisk(data) {
|
|
1232
|
+
if (!Array.isArray(data)) return JSON.stringify(data, null, 2);
|
|
1233
|
+
if (data.length === 0) return "(no risk factors found)";
|
|
1234
|
+
const rows = data.map((r) => [
|
|
1235
|
+
String(r.category ?? r.risk_type ?? "-"),
|
|
1236
|
+
truncate3(String(r.description ?? r.text ?? "-"), 80)
|
|
1059
1237
|
]);
|
|
1060
|
-
return makeTable(["
|
|
1238
|
+
return makeTable(["Category", "Description"], rows);
|
|
1239
|
+
}
|
|
1240
|
+
function truncate3(s, max) {
|
|
1241
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
1061
1242
|
}
|
|
1062
1243
|
|
|
1063
|
-
// src/commands/
|
|
1064
|
-
function
|
|
1244
|
+
// src/commands/drawdown.ts
|
|
1245
|
+
function registerDrawdownCommand(program2) {
|
|
1065
1246
|
addGlobalOptions(
|
|
1066
|
-
program2.command("
|
|
1067
|
-
).action(async (
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1247
|
+
program2.command("drawdown").description("Portfolio drawdown analysis").requiredOption("--symbols <s>", "Comma-separated symbols (e.g., AAPL,MSFT)").requiredOption("--allocations <a>", "Comma-separated weights (e.g., 0.5,0.5)")
|
|
1248
|
+
).action(async (opts) => {
|
|
1249
|
+
const data = await callRest("portfolios/drawdown/", {
|
|
1250
|
+
symbols: opts.symbols,
|
|
1251
|
+
allocations: opts.allocations
|
|
1252
|
+
});
|
|
1253
|
+
outputResult(data, opts, formatDrawdown);
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
function formatDrawdown(data) {
|
|
1257
|
+
const obj = data;
|
|
1258
|
+
if (!obj || typeof obj !== "object") return JSON.stringify(data, null, 2);
|
|
1259
|
+
const intervals = obj.interval_results ?? obj;
|
|
1260
|
+
const rows = [];
|
|
1261
|
+
for (const [interval, dd] of Object.entries(intervals)) {
|
|
1262
|
+
if (typeof dd !== "object" || !dd) continue;
|
|
1263
|
+
const d = dd;
|
|
1264
|
+
let troughDate = "-";
|
|
1265
|
+
let recoveryDate = "not recovered";
|
|
1266
|
+
const plotData = d.plot_data;
|
|
1267
|
+
if (plotData && plotData.length > 0) {
|
|
1268
|
+
let minDD = 0;
|
|
1269
|
+
let minIdx = -1;
|
|
1270
|
+
for (let i = 0; i < plotData.length; i++) {
|
|
1271
|
+
if (plotData[i].drawdown < minDD) {
|
|
1272
|
+
minDD = plotData[i].drawdown;
|
|
1273
|
+
minIdx = i;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (minIdx >= 0) {
|
|
1277
|
+
troughDate = plotData[minIdx].date;
|
|
1278
|
+
for (let i = minIdx + 1; i < plotData.length; i++) {
|
|
1279
|
+
if (plotData[i].drawdown >= 0) {
|
|
1280
|
+
recoveryDate = plotData[i].date;
|
|
1281
|
+
break;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1071
1285
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1286
|
+
rows.push([
|
|
1287
|
+
interval,
|
|
1288
|
+
d.max_drawdown != null ? `${(Number(d.max_drawdown) * 100).toFixed(2)}%` : "-",
|
|
1289
|
+
troughDate,
|
|
1290
|
+
recoveryDate
|
|
1291
|
+
]);
|
|
1292
|
+
}
|
|
1293
|
+
if (rows.length === 0) return JSON.stringify(data, null, 2);
|
|
1294
|
+
return makeTable(["Interval", "Max Drawdown", "Trough Date", "Recovery"], rows);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// src/commands/macro.ts
|
|
1298
|
+
function registerMacroCommand(program2) {
|
|
1299
|
+
addGlobalOptions(
|
|
1300
|
+
program2.command("macro").description("Macroeconomic indicators (GDP, CPI, unemployment, fed funds, treasury, inflation)").option("--indicators <list>", "Comma-separated: GDP,CPI,UNEMPLOYMENT,FED_FUNDS,TREASURY_10Y,INFLATION")
|
|
1301
|
+
).addHelpText("after", `
|
|
1302
|
+
Examples:
|
|
1303
|
+
$ bloom macro All macro indicators
|
|
1304
|
+
$ bloom macro --indicators GDP,CPI,UNEMPLOYMENT Specific indicators only
|
|
1305
|
+
$ bloom macro --indicators FED_FUNDS,TREASURY_10Y Interest rates
|
|
1306
|
+
`).action(async (opts) => {
|
|
1307
|
+
const args = {};
|
|
1308
|
+
if (opts.indicators) {
|
|
1309
|
+
args.indicators = opts.indicators;
|
|
1076
1310
|
}
|
|
1077
|
-
const
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1311
|
+
const data = await callTool("get_macro_indicators", args);
|
|
1312
|
+
outputResult(data, opts, formatMacro);
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
function formatMacro(data) {
|
|
1316
|
+
const obj = data;
|
|
1317
|
+
const rows = [];
|
|
1318
|
+
for (const [name, raw] of Object.entries(obj)) {
|
|
1319
|
+
const d = raw;
|
|
1320
|
+
if (d.error) {
|
|
1321
|
+
rows.push([name, "-", "-", "-", "-"]);
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1324
|
+
const points = d.data;
|
|
1325
|
+
if (!points || points.length === 0) {
|
|
1326
|
+
rows.push([name, "-", "-", "-", "-"]);
|
|
1327
|
+
continue;
|
|
1089
1328
|
}
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1329
|
+
const latest = points[0];
|
|
1330
|
+
const prev = points.length > 1 ? points[1] : null;
|
|
1331
|
+
const latestVal = latest.value;
|
|
1332
|
+
const prevVal = prev?.value ?? "-";
|
|
1333
|
+
const change = latestVal !== "." && prevVal !== "-" && prevVal !== "." ? (Number(latestVal) - Number(prevVal)).toFixed(2) : "-";
|
|
1334
|
+
rows.push([name, String(d.unit ?? ""), latestVal, latest.date, change]);
|
|
1335
|
+
}
|
|
1336
|
+
return makeTable(["Indicator", "Unit", "Latest", "Date", "Change"], rows);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// src/commands/sectors.ts
|
|
1340
|
+
function registerSectorsCommand(program2) {
|
|
1341
|
+
addGlobalOptions(
|
|
1342
|
+
program2.command("sectors").description("Sector performance across time periods")
|
|
1343
|
+
).addHelpText("after", `
|
|
1344
|
+
Examples:
|
|
1345
|
+
$ bloom sectors Sector performance across all time periods
|
|
1346
|
+
$ bloom sectors -f json Output as JSON
|
|
1347
|
+
`).action(async (opts) => {
|
|
1348
|
+
const data = await callTool("get_sector_performance", {});
|
|
1349
|
+
outputResult(data, opts, formatSectors);
|
|
1092
1350
|
});
|
|
1093
1351
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1352
|
+
var PERIOD_KEYS = [
|
|
1353
|
+
"Rank A: Real-Time Performance",
|
|
1354
|
+
"Rank B: 1 Day Performance",
|
|
1355
|
+
"Rank C: 5 Day Performance",
|
|
1356
|
+
"Rank D: 1 Month Performance",
|
|
1357
|
+
"Rank E: 3 Month Performance",
|
|
1358
|
+
"Rank F: Year-to-Date (YTD) Performance",
|
|
1359
|
+
"Rank G: 1 Year Performance",
|
|
1360
|
+
"Rank H: 3 Year Performance",
|
|
1361
|
+
"Rank I: 5 Year Performance",
|
|
1362
|
+
"Rank J: 10 Year Performance"
|
|
1363
|
+
];
|
|
1364
|
+
var PERIOD_LABELS = [
|
|
1365
|
+
"Real-Time",
|
|
1366
|
+
"1 Day",
|
|
1367
|
+
"5 Day",
|
|
1368
|
+
"1 Month",
|
|
1369
|
+
"3 Month",
|
|
1370
|
+
"YTD",
|
|
1371
|
+
"1 Year",
|
|
1372
|
+
"3 Year",
|
|
1373
|
+
"5 Year",
|
|
1374
|
+
"10 Year"
|
|
1375
|
+
];
|
|
1376
|
+
function formatSectors(data) {
|
|
1096
1377
|
const obj = data;
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1378
|
+
const sectors = /* @__PURE__ */ new Set();
|
|
1379
|
+
for (const key of PERIOD_KEYS) {
|
|
1380
|
+
const period = obj[key];
|
|
1381
|
+
if (period) {
|
|
1382
|
+
for (const sector of Object.keys(period)) sectors.add(sector);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
if (sectors.size === 0) return JSON.stringify(data, null, 2);
|
|
1386
|
+
const sectorList = Array.from(sectors).sort();
|
|
1387
|
+
const rows = sectorList.map((sector) => {
|
|
1388
|
+
const vals = PERIOD_KEYS.map((key) => {
|
|
1389
|
+
const v = obj[key]?.[sector];
|
|
1390
|
+
return v ?? "-";
|
|
1391
|
+
});
|
|
1392
|
+
return [sector, ...vals];
|
|
1393
|
+
});
|
|
1394
|
+
return makeTable(["Sector", ...PERIOD_LABELS], rows);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// src/commands/dcf.ts
|
|
1398
|
+
function registerDcfCommand(program2) {
|
|
1399
|
+
addGlobalOptions(
|
|
1400
|
+
program2.command("dcf").description("Discounted cash flow valuation").argument("<symbol>", "Ticker symbol")
|
|
1401
|
+
).addHelpText("after", `
|
|
1402
|
+
Examples:
|
|
1403
|
+
$ bloom dcf AAPL Discounted cash flow valuation for Apple
|
|
1404
|
+
$ bloom dcf TSLA DCF valuation for Tesla
|
|
1405
|
+
`).action(async (symbol, opts) => {
|
|
1406
|
+
const sym = symbol.toUpperCase();
|
|
1407
|
+
const result = await callTool("get_dcf_valuation", { symbol: sym });
|
|
1408
|
+
outputResult(result, opts, () => formatDcf(result, sym));
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
function formatDcf(data, symbol) {
|
|
1412
|
+
const rows = [];
|
|
1413
|
+
const obj = data;
|
|
1414
|
+
const dcf = obj?.dcf;
|
|
1415
|
+
const d = Array.isArray(dcf) ? dcf[0] : dcf;
|
|
1416
|
+
if (d && typeof d === "object") {
|
|
1417
|
+
const price = Number(d.Stock_Price ?? d.stockPrice ?? 0);
|
|
1418
|
+
const dcfVal = Number(d.DCF ?? d.dcf ?? 0);
|
|
1419
|
+
const upside = price > 0 ? ((dcfVal - price) / price * 100).toFixed(1) : "-";
|
|
1420
|
+
rows.push(["Current Price", `$${price.toFixed(2)}`]);
|
|
1421
|
+
rows.push(["DCF Value", `$${dcfVal.toFixed(2)}`]);
|
|
1422
|
+
rows.push(["Upside/Downside", `${upside}%`]);
|
|
1423
|
+
}
|
|
1424
|
+
if (rows.length === 0) return `No DCF data available for ${symbol}`;
|
|
1425
|
+
return makeTable(["Metric", "Value"], rows);
|
|
1426
|
+
}
|
|
1101
1427
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1428
|
+
// src/commands/holders.ts
|
|
1429
|
+
function registerHoldersCommand(program2) {
|
|
1430
|
+
addGlobalOptions(
|
|
1431
|
+
program2.command("holders").description("Institutional and mutual fund holders").argument("<symbol>", "Ticker symbol").option("--type <t>", "institutional or mutual-fund (default: both)").option("--limit <n>", "Max holders to show", "15")
|
|
1432
|
+
).addHelpText("after", `
|
|
1433
|
+
Examples:
|
|
1434
|
+
$ bloom holders AAPL All holders (institutional + mutual fund)
|
|
1435
|
+
$ bloom holders TSLA --type institutional Institutional holders only
|
|
1436
|
+
$ bloom holders NVDA --limit 5 Top 5 holders
|
|
1437
|
+
`).action(async (symbol, opts) => {
|
|
1438
|
+
const sym = symbol.toUpperCase();
|
|
1439
|
+
const limit = parseIntSafe(opts.limit, 15);
|
|
1440
|
+
const args = { symbol: sym };
|
|
1441
|
+
if (opts.type) args.holder_type = opts.type;
|
|
1442
|
+
const data = await callTool("get_holders", args);
|
|
1443
|
+
outputResult(data, opts, () => formatHolders(data, limit));
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
function formatHolders(data, limit) {
|
|
1447
|
+
const parts = [];
|
|
1448
|
+
const addSection = (title, list) => {
|
|
1449
|
+
const items = list;
|
|
1450
|
+
if (items && items.length > 0) {
|
|
1451
|
+
const rows = items.slice(0, limit).map((h) => [
|
|
1452
|
+
String(h.holder ?? "-"),
|
|
1453
|
+
fmtNum2(h.shares),
|
|
1454
|
+
h.dateReported ? String(h.dateReported).slice(0, 10) : "-",
|
|
1455
|
+
fmtNum2(h.change)
|
|
1456
|
+
]);
|
|
1457
|
+
parts.push(`${title}
|
|
1458
|
+
${makeTable(["Holder", "Shares", "Date", "Change"], rows)}`);
|
|
1108
1459
|
}
|
|
1460
|
+
};
|
|
1461
|
+
addSection("INSTITUTIONAL HOLDERS", data.institutional);
|
|
1462
|
+
addSection("MUTUAL FUND HOLDERS", data.mutual_fund);
|
|
1463
|
+
return parts.length > 0 ? parts.join("\n\n") : "(no holder data found)";
|
|
1464
|
+
}
|
|
1465
|
+
function fmtNum2(v) {
|
|
1466
|
+
if (v == null) return "-";
|
|
1467
|
+
const n = Number(v);
|
|
1468
|
+
if (isNaN(n)) return String(v);
|
|
1469
|
+
if (Math.abs(n) >= 1e9) return `${(n / 1e9).toFixed(2)}B`;
|
|
1470
|
+
if (Math.abs(n) >= 1e6) return `${(n / 1e6).toFixed(2)}M`;
|
|
1471
|
+
if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
1472
|
+
return n.toLocaleString();
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/commands/short-interest.ts
|
|
1476
|
+
function registerShortInterestCommand(program2) {
|
|
1477
|
+
addGlobalOptions(
|
|
1478
|
+
program2.command("short-interest").description("Short interest history").argument("<symbol>", "Ticker symbol").option("--limit <n>", "Number of records", "10")
|
|
1479
|
+
).addHelpText("after", `
|
|
1480
|
+
Examples:
|
|
1481
|
+
$ bloom short-interest GME Short interest history for GameStop
|
|
1482
|
+
$ bloom short-interest AMC --limit 20 Last 20 short interest records
|
|
1483
|
+
`).action(async (symbol, opts) => {
|
|
1484
|
+
const sym = symbol.toUpperCase();
|
|
1485
|
+
const limit = parseIntSafe(opts.limit, 10);
|
|
1486
|
+
const data = await callTool("get_short_interest", { symbol: sym, limit });
|
|
1487
|
+
outputResult(data, opts, formatShort);
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
function formatShort(data) {
|
|
1491
|
+
if (!Array.isArray(data) || data.length === 0) return "(no short interest data)";
|
|
1492
|
+
const rows = data.map((r) => [
|
|
1493
|
+
String(r.date ?? "-"),
|
|
1494
|
+
fmtNum3(r.shortInterest),
|
|
1495
|
+
String(r.daysToCover ?? r.daysTocover ?? "-"),
|
|
1496
|
+
String(r.shortInterestRatio ?? "-"),
|
|
1497
|
+
fmtNum3(r.shortPriorMonthInterest ?? r.shortPriorMonth),
|
|
1498
|
+
r.changePercent != null ? `${Number(r.changePercent).toFixed(2)}%` : "-"
|
|
1499
|
+
]);
|
|
1500
|
+
return makeTable(["Date", "Short Interest", "Days to Cover", "Short Ratio", "Prior Month", "Change %"], rows);
|
|
1501
|
+
}
|
|
1502
|
+
function fmtNum3(v) {
|
|
1503
|
+
if (v == null) return "-";
|
|
1504
|
+
const n = Number(v);
|
|
1505
|
+
if (isNaN(n)) return String(v);
|
|
1506
|
+
if (Math.abs(n) >= 1e9) return `${(n / 1e9).toFixed(2)}B`;
|
|
1507
|
+
if (Math.abs(n) >= 1e6) return `${(n / 1e6).toFixed(2)}M`;
|
|
1508
|
+
if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
1509
|
+
return n.toLocaleString();
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
// src/commands/insider.ts
|
|
1513
|
+
function registerInsiderCommand(program2) {
|
|
1514
|
+
addGlobalOptions(
|
|
1515
|
+
program2.command("insider").description("Insider trading activity (SEC Form 4)").argument("<symbol>", "Ticker symbol").option("--limit <n>", "Max transactions to show", "20")
|
|
1516
|
+
).addHelpText("after", `
|
|
1517
|
+
Examples:
|
|
1518
|
+
$ bloom insider AAPL Recent insider trades for Apple
|
|
1519
|
+
$ bloom insider TSLA --limit 5 Last 5 insider transactions
|
|
1520
|
+
`).action(async (symbol, opts) => {
|
|
1521
|
+
const sym = symbol.toUpperCase();
|
|
1522
|
+
const limit = parseIntSafe(opts.limit, 20);
|
|
1523
|
+
const data = await callTool("get_insider_trading", { symbol: sym, limit });
|
|
1524
|
+
outputResult(data, opts, formatInsider);
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
function formatInsider(data) {
|
|
1528
|
+
if (!Array.isArray(data) || data.length === 0) return "(no insider trading data)";
|
|
1529
|
+
const rows = data.map((t) => {
|
|
1530
|
+
const val = t.securitiesTransacted && t.price ? `$${(Number(t.securitiesTransacted) * Number(t.price)).toLocaleString()}` : "-";
|
|
1531
|
+
return [
|
|
1532
|
+
String(t.filingDate ?? t.transactionDate ?? "-").slice(0, 10),
|
|
1533
|
+
String(t.reportingName ?? "-"),
|
|
1534
|
+
String(t.typeOfOwner ?? "-"),
|
|
1535
|
+
String(t.transactionType ?? t.acquistionOrDisposition ?? "-"),
|
|
1536
|
+
fmtNum4(t.securitiesTransacted),
|
|
1537
|
+
t.price != null ? `$${Number(t.price).toFixed(2)}` : "-",
|
|
1538
|
+
val
|
|
1539
|
+
];
|
|
1540
|
+
});
|
|
1541
|
+
return makeTable(["Date", "Name", "Title", "Type", "Shares", "Price", "Value"], rows);
|
|
1542
|
+
}
|
|
1543
|
+
function fmtNum4(v) {
|
|
1544
|
+
if (v == null) return "-";
|
|
1545
|
+
const n = Number(v);
|
|
1546
|
+
if (isNaN(n)) return String(v);
|
|
1547
|
+
return n.toLocaleString();
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
// src/commands/dividends.ts
|
|
1551
|
+
function registerDividendsCommand(program2) {
|
|
1552
|
+
addGlobalOptions(
|
|
1553
|
+
program2.command("dividends").description("Dividend history for a stock").argument("<symbol>", "Ticker symbol").option("--limit <n>", "Number of records", "20").option("--from <date>", "Start date (YYYY-MM-DD)")
|
|
1554
|
+
).addHelpText("after", `
|
|
1555
|
+
Examples:
|
|
1556
|
+
$ bloom dividends KO Dividend history for Coca-Cola
|
|
1557
|
+
$ bloom dividends AAPL --limit 8 Last 8 dividends
|
|
1558
|
+
$ bloom dividends JNJ --from 2024-01-01 Dividends since Jan 2024
|
|
1559
|
+
`).action(async (symbol, opts) => {
|
|
1560
|
+
const sym = symbol.toUpperCase();
|
|
1561
|
+
const limit = parseIntSafe(opts.limit, 20);
|
|
1562
|
+
const args = { symbol: sym, limit };
|
|
1563
|
+
if (opts.from) args.from_date = opts.from;
|
|
1564
|
+
const data = await callTool("get_dividends", args);
|
|
1565
|
+
outputResult(data, opts, formatDividends);
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
function formatDividends(data) {
|
|
1569
|
+
if (!Array.isArray(data) || data.length === 0) return "(no dividend data)";
|
|
1570
|
+
const rows = data.map((d) => [
|
|
1571
|
+
String(d.date ?? "-"),
|
|
1572
|
+
d.value != null ? `$${Number(d.value).toFixed(4)}` : "-",
|
|
1573
|
+
String(d.declarationDate ?? "-"),
|
|
1574
|
+
String(d.recordDate ?? "-"),
|
|
1575
|
+
String(d.paymentDate ?? "-"),
|
|
1576
|
+
String(d.period ?? "-")
|
|
1577
|
+
]);
|
|
1578
|
+
return makeTable(["Ex-Date", "Amount", "Declaration", "Record", "Payment", "Period"], rows);
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// src/commands/splits.ts
|
|
1582
|
+
function registerSplitsCommand(program2) {
|
|
1583
|
+
addGlobalOptions(
|
|
1584
|
+
program2.command("splits").description("Stock split history").argument("<symbol>", "Ticker symbol").option("--from <date>", "Start date (YYYY-MM-DD)")
|
|
1585
|
+
).addHelpText("after", `
|
|
1586
|
+
Examples:
|
|
1587
|
+
$ bloom splits AAPL Stock split history for Apple
|
|
1588
|
+
$ bloom splits TSLA --from 2020-01-01 Splits since 2020
|
|
1589
|
+
`).action(async (symbol, opts) => {
|
|
1590
|
+
const sym = symbol.toUpperCase();
|
|
1591
|
+
const args = { symbol: sym };
|
|
1592
|
+
if (opts.from) args.from_date = opts.from;
|
|
1593
|
+
const data = await callTool("get_splits", args);
|
|
1594
|
+
outputResult(data, opts, formatSplits);
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
function formatSplits(data) {
|
|
1598
|
+
if (!Array.isArray(data) || data.length === 0) return "(no stock splits found)";
|
|
1599
|
+
const rows = data.map((s) => [
|
|
1600
|
+
String(s.date ?? "-"),
|
|
1601
|
+
String(s.split ?? "-")
|
|
1602
|
+
]);
|
|
1603
|
+
return makeTable(["Date", "Split Ratio"], rows);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
// src/commands/ipo.ts
|
|
1607
|
+
function registerIpoCommand(program2) {
|
|
1608
|
+
addGlobalOptions(
|
|
1609
|
+
program2.command("ipo").description("Upcoming and recent IPOs").option("--from <date>", "Start date (YYYY-MM-DD)").option("--to <date>", "End date (YYYY-MM-DD)")
|
|
1610
|
+
).addHelpText("after", `
|
|
1611
|
+
Examples:
|
|
1612
|
+
$ bloom ipo Recent and upcoming IPOs
|
|
1613
|
+
$ bloom ipo --from 2025-01-01 --to 2025-06-30 IPOs in H1 2025
|
|
1614
|
+
`).action(async (opts) => {
|
|
1615
|
+
const args = {};
|
|
1616
|
+
if (opts.from) args.from_date = opts.from;
|
|
1617
|
+
if (opts.to) args.to_date = opts.to;
|
|
1618
|
+
const data = await callTool("get_ipo_calendar", args);
|
|
1619
|
+
outputResult(data, opts, formatIpos);
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
function formatIpos(data) {
|
|
1623
|
+
const ipos = Array.isArray(data) ? data : data?.ipos;
|
|
1624
|
+
if (!Array.isArray(ipos) || ipos.length === 0) return "(no IPO data found)";
|
|
1625
|
+
const rows = ipos.map((ipo) => [
|
|
1626
|
+
String(ipo.date ?? ipo.start_date ?? "-"),
|
|
1627
|
+
String(ipo.name ?? ipo.company ?? "-"),
|
|
1628
|
+
String(ipo.code ?? ipo.ticker ?? ipo.symbol ?? "-"),
|
|
1629
|
+
String(ipo.exchange ?? "-"),
|
|
1630
|
+
ipo.price_from && ipo.price_to ? `$${ipo.price_from}-$${ipo.price_to}` : ipo.price ? `$${ipo.price}` : "-",
|
|
1631
|
+
ipo.shares ? fmtNum5(ipo.shares) : "-"
|
|
1632
|
+
]);
|
|
1633
|
+
return makeTable(["Date", "Company", "Symbol", "Exchange", "Price Range", "Shares"], rows);
|
|
1634
|
+
}
|
|
1635
|
+
function fmtNum5(v) {
|
|
1636
|
+
if (v == null) return "-";
|
|
1637
|
+
const n = Number(v);
|
|
1638
|
+
if (isNaN(n)) return String(v);
|
|
1639
|
+
if (Math.abs(n) >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
1640
|
+
return n.toLocaleString();
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// src/commands/compare.ts
|
|
1644
|
+
function registerCompareCommand(program2) {
|
|
1645
|
+
addGlobalOptions(
|
|
1646
|
+
program2.command("compare").description("Side-by-side comparison of stocks").argument("<symbols...>", "Ticker symbols (2-10)")
|
|
1647
|
+
).addHelpText("after", `
|
|
1648
|
+
Examples:
|
|
1649
|
+
$ bloom compare AAPL MSFT Side-by-side comparison
|
|
1650
|
+
$ bloom compare AAPL MSFT GOOGL AMZN Compare big tech stocks
|
|
1651
|
+
$ bloom compare TSLA RIVN LCID Compare EV manufacturers
|
|
1652
|
+
`).action(async (symbols, opts) => {
|
|
1653
|
+
const syms = symbols.map((s) => s.toUpperCase()).slice(0, 10);
|
|
1654
|
+
const data = await callTool("get_symbols_metadata", { symbols: syms });
|
|
1655
|
+
outputResult(data, opts, (d) => formatComparison(d, syms));
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
function formatComparison(data, symbols) {
|
|
1659
|
+
const obj = data;
|
|
1660
|
+
const list = obj?.symbols ?? (Array.isArray(data) ? data : null);
|
|
1661
|
+
if (!Array.isArray(list) || list.length === 0) return JSON.stringify(data, null, 2);
|
|
1662
|
+
const metrics = [
|
|
1663
|
+
["Price", "latest_price", "$"],
|
|
1664
|
+
["Market Cap", "market_cap", "cap"],
|
|
1665
|
+
["P/E", "pe_ratio", ""],
|
|
1666
|
+
["EPS", "eps", "$"],
|
|
1667
|
+
["Dividend Yield", "dividend_yield", "%"],
|
|
1668
|
+
["52W High", "week_52_high", "$"],
|
|
1669
|
+
["52W Low", "week_52_low", "$"],
|
|
1670
|
+
["Beta", "beta", ""],
|
|
1671
|
+
["Sector", "sector", ""]
|
|
1672
|
+
];
|
|
1673
|
+
const bySymbol = {};
|
|
1674
|
+
for (const item of list) {
|
|
1675
|
+
const i = item;
|
|
1676
|
+
const meta = i.basic_metadata ?? i;
|
|
1677
|
+
const sym = String(meta.symbol ?? i.symbol ?? "");
|
|
1678
|
+
bySymbol[sym] = meta;
|
|
1679
|
+
}
|
|
1680
|
+
const headers = ["Metric", ...symbols];
|
|
1681
|
+
const rows = metrics.map(([label, key, fmt2]) => {
|
|
1682
|
+
const vals = symbols.map((s) => {
|
|
1683
|
+
const meta = bySymbol[s];
|
|
1684
|
+
if (!meta) return "-";
|
|
1685
|
+
const v = meta[key];
|
|
1686
|
+
if (v == null) return "-";
|
|
1687
|
+
if (fmt2 === "$") return `$${Number(v).toFixed(2)}`;
|
|
1688
|
+
if (fmt2 === "%") return `${(Number(v) * 100).toFixed(2)}%`;
|
|
1689
|
+
if (fmt2 === "cap") return fmtCap(v);
|
|
1690
|
+
return String(v);
|
|
1691
|
+
});
|
|
1692
|
+
return [label, ...vals];
|
|
1693
|
+
});
|
|
1694
|
+
return makeTable(headers, rows);
|
|
1695
|
+
}
|
|
1696
|
+
function fmtCap(v) {
|
|
1697
|
+
const n = Number(v);
|
|
1698
|
+
if (isNaN(n)) return String(v);
|
|
1699
|
+
if (n >= 1e12) return `$${(n / 1e12).toFixed(2)}T`;
|
|
1700
|
+
if (n >= 1e9) return `$${(n / 1e9).toFixed(2)}B`;
|
|
1701
|
+
if (n >= 1e6) return `$${(n / 1e6).toFixed(0)}M`;
|
|
1702
|
+
return `$${n.toLocaleString()}`;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
// src/commands/ratings.ts
|
|
1706
|
+
function registerRatingsCommand(program2) {
|
|
1707
|
+
addGlobalOptions(
|
|
1708
|
+
program2.command("ratings").description("Analyst ratings, grades, and price targets").argument("<symbol>", "Ticker symbol").option("--limit <n>", "Max records to show", "20")
|
|
1709
|
+
).addHelpText("after", `
|
|
1710
|
+
Examples:
|
|
1711
|
+
$ bloom ratings AAPL Analyst ratings and price targets
|
|
1712
|
+
$ bloom ratings NVDA --limit 5 Last 5 analyst actions
|
|
1713
|
+
`).action(async (symbol, opts) => {
|
|
1714
|
+
const sym = symbol.toUpperCase();
|
|
1715
|
+
const limit = parseIntSafe(opts.limit, 20);
|
|
1716
|
+
const result = await callTool("get_analyst_ratings", { symbol: sym, limit });
|
|
1717
|
+
outputResult(result, opts, () => {
|
|
1718
|
+
const obj = result;
|
|
1719
|
+
return formatRatings(obj.grades, obj.estimates);
|
|
1720
|
+
});
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
function formatRatings(grades, estimates) {
|
|
1724
|
+
const parts = [];
|
|
1725
|
+
if (Array.isArray(grades) && grades.length > 0) {
|
|
1726
|
+
const rows = grades.map((g) => [
|
|
1727
|
+
String(g.date ?? "-").slice(0, 10),
|
|
1728
|
+
String(g.gradingCompany ?? "-"),
|
|
1729
|
+
String(g.previousGrade ?? "-"),
|
|
1730
|
+
String(g.newGrade ?? "-"),
|
|
1731
|
+
String(g.action ?? "-")
|
|
1732
|
+
]);
|
|
1733
|
+
parts.push("ANALYST GRADES\n" + makeTable(["Date", "Firm", "Previous", "New", "Action"], rows));
|
|
1109
1734
|
}
|
|
1110
|
-
|
|
1735
|
+
if (Array.isArray(estimates) && estimates.length > 0) {
|
|
1736
|
+
const rows = estimates.map((e) => [
|
|
1737
|
+
String(e.date ?? "-").slice(0, 10),
|
|
1738
|
+
e.estimatedRevenueAvg != null ? fmtNum6(e.estimatedRevenueAvg) : "-",
|
|
1739
|
+
e.estimatedEpsAvg != null ? `$${Number(e.estimatedEpsAvg).toFixed(2)}` : "-",
|
|
1740
|
+
String(e.numberAnalystEstimatedRevenue ?? e.numberAnalystsEstimatedRevenue ?? "-")
|
|
1741
|
+
]);
|
|
1742
|
+
parts.push("ANALYST ESTIMATES\n" + makeTable(["Period", "Est. Revenue", "Est. EPS", "# Analysts"], rows));
|
|
1743
|
+
}
|
|
1744
|
+
return parts.length > 0 ? parts.join("\n\n") : "(no ratings data found)";
|
|
1745
|
+
}
|
|
1746
|
+
function fmtNum6(v) {
|
|
1747
|
+
if (v == null) return "-";
|
|
1748
|
+
const n = Number(v);
|
|
1749
|
+
if (isNaN(n)) return String(v);
|
|
1750
|
+
if (Math.abs(n) >= 1e12) return `$${(n / 1e12).toFixed(2)}T`;
|
|
1751
|
+
if (Math.abs(n) >= 1e9) return `$${(n / 1e9).toFixed(2)}B`;
|
|
1752
|
+
if (Math.abs(n) >= 1e6) return `$${(n / 1e6).toFixed(0)}M`;
|
|
1753
|
+
return `$${n.toLocaleString()}`;
|
|
1111
1754
|
}
|
|
1112
1755
|
|
|
1113
1756
|
// src/index.ts
|
|
1114
1757
|
var program = new Command();
|
|
1115
1758
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
1116
1759
|
var pkgPath = path.join(__dirname, "..", "package.json");
|
|
1117
|
-
var compiledVersion = "0.1.
|
|
1760
|
+
var compiledVersion = "0.1.6".length > 0 ? "0.1.6" : void 0;
|
|
1118
1761
|
var envVersion = process.env.BLOOM_CLI_VERSION ?? process.env.npm_package_version;
|
|
1119
1762
|
var fallbackVersion = compiledVersion ?? "unknown";
|
|
1120
1763
|
var cliVersion = envVersion ?? fallbackVersion;
|
|
@@ -1139,6 +1782,7 @@ registerMarketCommand(program);
|
|
|
1139
1782
|
registerCollectionsCommand(program);
|
|
1140
1783
|
registerScreenCommand(program);
|
|
1141
1784
|
registerEarningsCommand(program);
|
|
1785
|
+
registerEarningsCalendarCommand(program);
|
|
1142
1786
|
registerTechnicalsCommand(program);
|
|
1143
1787
|
registerOptionsCommand(program);
|
|
1144
1788
|
registerPortfolioCommand(program);
|
|
@@ -1149,8 +1793,21 @@ registerTranscriptCommand(program);
|
|
|
1149
1793
|
registerAiTradesCommand(program);
|
|
1150
1794
|
registerAiPortfolioCommand(program);
|
|
1151
1795
|
registerMaCommand(program);
|
|
1152
|
-
|
|
1153
|
-
|
|
1796
|
+
registerBacktestCommand(program);
|
|
1797
|
+
registerCorrelationCommand(program);
|
|
1798
|
+
registerRiskCommand(program);
|
|
1799
|
+
registerDrawdownCommand(program);
|
|
1800
|
+
registerMacroCommand(program);
|
|
1801
|
+
registerSectorsCommand(program);
|
|
1802
|
+
registerDcfCommand(program);
|
|
1803
|
+
registerHoldersCommand(program);
|
|
1804
|
+
registerShortInterestCommand(program);
|
|
1805
|
+
registerInsiderCommand(program);
|
|
1806
|
+
registerDividendsCommand(program);
|
|
1807
|
+
registerSplitsCommand(program);
|
|
1808
|
+
registerIpoCommand(program);
|
|
1809
|
+
registerCompareCommand(program);
|
|
1810
|
+
registerRatingsCommand(program);
|
|
1154
1811
|
program.parseAsync().catch((err) => {
|
|
1155
1812
|
console.error("Error:", err.message || err);
|
|
1156
1813
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloomai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "CLI tool for Bloom financial data - download market data to files for analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsup",
|
|
16
|
-
"dev": "tsup --watch"
|
|
16
|
+
"dev": "tsup --watch",
|
|
17
|
+
"test": "vitest run"
|
|
17
18
|
},
|
|
18
19
|
"keywords": [
|
|
19
20
|
"finance",
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@types/node": "^22.13.0",
|
|
32
33
|
"tsup": "^8.4.0",
|
|
33
|
-
"typescript": "^5.7.0"
|
|
34
|
+
"typescript": "^5.7.0",
|
|
35
|
+
"vitest": "^4.1.5"
|
|
34
36
|
}
|
|
35
37
|
}
|