sentisense 0.61.0 → 0.63.0

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 CHANGED
@@ -68,7 +68,7 @@ npx -y sentisense@latest health
68
68
  |---------|--------------|
69
69
  | `auth [key]` | Store a key, show what is configured, or `--remove` it |
70
70
  | `health` | Reachability, key validity, latency, and the resolved base URL |
71
- | `quote <ticker>...` | Price, day range, 52-week range, market cap, P/E. One request per ticker |
71
+ | `quote <ticker>...` | Price, day range, 52-week range, market cap, P/E. One request per ticker; ETFs show AUM, expense ratio and NAV |
72
72
  | `sentiment <ticker>` | SentiSense Score, tone, attention, per-source breakdown, `--days N` history |
73
73
  | `mood` | Composite market sentiment, the signals behind it, and the sector map |
74
74
  | `analysts <ticker>` | Consensus, price target band, recent upgrades and downgrades. `--coverage` for who covers it, by firm |
@@ -238,7 +238,10 @@ client.stocks.getProfile("AAPL") // Company profile
238
238
  client.stocks.getChart("AAPL", { timeframe: "6M" }) // OHLCV chart data
239
239
  client.stocks.getMarketStatus() // Market open/closed
240
240
  client.stocks.getFundamentals("AAPL") // Financial data
241
- client.stocks.getShortInterest("GME") // Short interest
241
+ client.stocks.getShortInterest("GME", { limit: 12 }) // Short interest by settlement date
242
+ client.stocks.getShortVolume("GME") // Daily short volume
243
+ client.stocks.getFloat("GME") // Free float
244
+ client.stocks.getGraph("AAPL", { depth: 1 }) // Company knowledge graph: people, products, peers
242
245
  client.stocks.getOptionsSummary("NVDA") // End-of-day options dossier
243
246
  client.stocks.getOptionsHistory("NVDA", { window: "2y" }) // Daily options aggregates over time
244
247
  client.stocks.getRating("AAPL") // SentiSense Rating: score, letter, percentile, dimensions
@@ -247,6 +250,10 @@ client.stocks.getAISummary("AAPL", { depth: "deep" }) // AI report (PRO)
247
250
 
248
251
  Price fields carry `priceAsOf` (Unix milliseconds) for the age of the market data; read that for freshness rather than `timestamp`, which is when the response was served.
249
252
 
253
+ Short interest and short volume come back newest first, and an unknown ticker returns an empty `dataPoints` array rather than an error. Every identifier in `getGraph` is a slug, the same handle the metric and document endpoints take. An ETF symbol is refused by `getQuote` with an `APIError` whose `code` is `"ticker_is_etf"`; use `etfs.quote()` for funds.
254
+
255
+ Product nodes (`type === "PRODUCT_OR_SERVICE"`) in `stocks.getGraph` now carry optional `category` when known. Other nodes and products without a category omit it.
256
+
250
257
  ### Documents & news
251
258
 
252
259
  ```typescript
@@ -287,6 +294,18 @@ client.institutional.getHolders("AAPL", "2025-02-14")
287
294
  client.institutional.getActivists("2025-02-14")
288
295
  ```
289
296
 
297
+ `positionsHeld` on an institution detail counts the full portfolio's held positions:
298
+ `max(0, holdingsCount - soldOutPositions)`, even when the holdings list is truncated.
299
+ `getInstitutionDetail` keeps its `Promise<unknown>` signature. The exported all-optional
300
+ `InstitutionDetail` type is available for callers who want to opt in:
301
+
302
+ ```typescript
303
+ import type { InstitutionDetail, PreviewResponse } from "sentisense";
304
+
305
+ const detail = await client.institutional.getInstitutionDetail("1067983") as PreviewResponse<InstitutionDetail>;
306
+ console.log(detail.data.positionsHeld); // undefined when an older response omits it
307
+ ```
308
+
290
309
  **Paging the holder list.** A widely held ticker returns thousands of rows: a megacap quarter is roughly 6,000 holders and 1.5 MB on the wire. Pass `limit` unless you really want the whole list; omitting the options object sends the original unbounded request, so existing code keeps working.
291
310
 
292
311
  | Option | Values |
@@ -412,6 +431,8 @@ Two shapes to read rather than assume. A firm can appear with `noteCount: 0`, a
412
431
 
413
432
  `ratingBuckets` sizes the same book by rating tier: `buy`, `hold`, `sell`, `unrated` and `total`, counted over every covering firm before the free truncation, so `buy + hold + sell + unrated === total` and a free key reads the same numbers as a PRO one. `unrated` is a desk with no current rating on record, such as a price-target-only firm. These count the firms in this coverage book, a different population from the `strongBuy` through `strongSell` figures on `client.analyst.consensus`, which come from the provider's analyst survey. Read one or the other, do not reconcile them.
414
433
 
434
+ Surprise rows now carry optional `surprisePct`, the signed true percent rounded half-up to two decimals: `(actualEps - estimateEps) * 100 / abs(estimateEps)`. It is `null` when either EPS is missing or the estimate is zero, and `undefined` on older responses. The existing `surprisePercent` fraction is unchanged (`0.03` means about 3%).
435
+
415
436
  ### Earnings
416
437
 
417
438
  The earnings analysis report is the assembled version of a quarter: one object per fiscal period carrying the editorial headline, the KPI cards with year-over-year deltas, the guidance language as management phrased it, and a summary of the earnings call. Pair it with the recent-reporters feed to drive a post-earnings sweep. Summaries, recent reports, statistics, and rankings return the preview envelope; per-ticker reactions return a direct payload.
@@ -446,6 +467,8 @@ if (quarter) {
446
467
 
447
468
  The forward-looking half of the family is `client.calendar.getEarnings()`, which covers scheduled dates and consensus EPS rather than results.
448
469
 
470
+ `earnings.getSummaries` exposes optional `totalCount` on the envelope on both PRO and FREE. It counts indexed quarters; returned rows can be fewer because of `limit` or an unavailable body. Older responses without the count still read as `undefined`.
471
+
449
472
  ### Company KPIs (PRO)
450
473
 
451
474
  ```typescript
@@ -459,6 +482,7 @@ Composition data is public; the holdings-weighted aggregate views follow the sam
459
482
 
460
483
  ```typescript
461
484
  client.etfs.list() // Every ETF tracked
485
+ client.etfs.quote("SPY") // Price, ranges, AUM, expense ratio, NAV
462
486
  client.etfs.holdings("QQQ") // Full composition + freshness metadata
463
487
  client.etfs.analystAggregate("QQQ") // Holdings-weighted analyst consensus
464
488
  client.etfs.insiderAggregate("ARKK", { lookbackDays: 90 }) // Holdings-weighted Form 4 net flow
@@ -505,6 +529,8 @@ During the trading session the dossier and the radar also carry a few intraday f
505
529
 
506
530
  A row whose baseline is still building carries its raw readings with the percentiles and `interestScore` omitted, which means "not enough history yet" rather than "nothing interesting". `getOptionsSummary` reports an uncovered ticker as a null payload inside the usual envelope, so the check is `result.data === null`: the response object itself is always truthy, and a bare `if (summary === null)` never fires. `getOptionsHistory` reports it as an empty `series` instead, so check the array's length rather than null-checking there.
507
531
 
532
+ On a delisted symbol, the options summary carries `listingStatus === "DELISTED"` and, when known, `delistedDate` (YYYY-MM-DD). This marks a frozen last dossier, so read `asOf` before treating it as current. Listed and pending symbols omit both fields.
533
+
508
534
  ### SentiSense Rating
509
535
 
510
536
  Where a stock ranks against the other stocks rated that day, as a score, a letter and a percentile, plus the seven dimensions the rank is blended from. It is a relative research signal for informational and educational purposes, not financial, investment or trading advice, and not a recommendation about any security. Every response carries the wording to display alongside a grade in `disclaimer`. [Methodology](https://sentisense.ai/methodology/#sentisense-rating).
package/dist/cli.cjs CHANGED
@@ -502,10 +502,22 @@ async function verifyTickerOnEmpty(api, ticker) {
502
502
  await api.stocks.getQuote(ticker);
503
503
  return void 0;
504
504
  } catch (error) {
505
+ if (isEtfRedirect(error)) return void 0;
505
506
  if (error instanceof NotFoundError) throw new UnknownTickerError(ticker);
506
507
  return `could not verify ${ticker}, so the empty result is unconfirmed`;
507
508
  }
508
509
  }
510
+ function isEtfRedirect(error) {
511
+ return error instanceof APIError && error.code === "ticker_is_etf";
512
+ }
513
+ async function quoteAnyTicker(api, ticker) {
514
+ try {
515
+ return { kind: "stock", quote: await api.stocks.getQuote(ticker) };
516
+ } catch (error) {
517
+ if (!isEtfRedirect(error)) throw error;
518
+ return { kind: "etf", quote: await api.etfs.quote(ticker) };
519
+ }
520
+ }
509
521
  var EMPTY_VERIFY_NOTE = "An empty result verifies the ticker before reporting no data, so a typo exits 4 rather";
510
522
  var EMPTY_VERIFY_NOTE_2 = "than looking like a company with nothing to report.";
511
523
 
@@ -518,11 +530,13 @@ function actionTone(actionType) {
518
530
  return void 0;
519
531
  }
520
532
  function primaryAnalyst(row) {
521
- const named = row.latestNote?.analyst;
522
- const match = (named ? row.analysts.find((analyst) => analyst.name === named) : void 0) ?? row.analysts[0];
523
- const name = match?.name ?? named ?? "";
533
+ const named = row.latestNote?.analyst ?? null;
534
+ const match = (named ? row.analysts.find(
535
+ (analyst) => named.slug !== null && analyst.slug === named.slug || analyst.name === named.name
536
+ ) : void 0) ?? row.analysts[0];
537
+ const name = match?.name ?? named?.name ?? "";
524
538
  const extra = row.analysts.length > 1 ? ` +${row.analysts.length - 1}` : "";
525
- return { name: name ? `${name}${extra}` : "", slug: match?.slug ?? "" };
539
+ return { name: name ? `${name}${extra}` : "", slug: match?.slug ?? named?.slug ?? "" };
526
540
  }
527
541
  var analystsCommand = {
528
542
  name: "analysts",
@@ -1331,7 +1345,7 @@ var flowsCommand = {
1331
1345
  };
1332
1346
 
1333
1347
  // src/version.ts
1334
- var VERSION = "0.61.0";
1348
+ var VERSION = "0.63.0";
1335
1349
 
1336
1350
  // src/resources/analyst.ts
1337
1351
  var Analyst = class {
@@ -1590,9 +1604,11 @@ var Earnings = class {
1590
1604
  * management phrased it, and a summary of the earnings call.
1591
1605
  *
1592
1606
  * Branch on `isPreview`: a PRO key receives every hydrated quarter in full, a
1593
- * FREE key receives the latest quarter shaped rather than truncated, plus
1594
- * `totalCount`. {@link EarningsQuarter} documents which fields each tier
1595
- * carries.
1607
+ * FREE key receives the latest quarter shaped rather than truncated.
1608
+ * `totalCount` on the envelope counts indexed quarters on both tiers. The
1609
+ * returned array can be shorter when a body is unavailable or limit cuts it.
1610
+ * Older responses may omit the count. {@link EarningsQuarter} documents
1611
+ * which fields each tier carries.
1596
1612
  *
1597
1613
  * A quarter typically appears within 48 hours of the company reporting, and
1598
1614
  * the call summary can arrive after the press-release content for the same
@@ -1726,6 +1742,19 @@ var Etfs = class {
1726
1742
  async list() {
1727
1743
  return this.client.get("/api/v1/etfs");
1728
1744
  }
1745
+ /**
1746
+ * Get the quote snapshot for an ETF: price, day and 52-week range, volume, dividend
1747
+ * yield, AUM, expense ratio, NAV and inception date.
1748
+ *
1749
+ * Fails with a 400 `APIError` whose `code` is `"ticker_is_not_etf"` when the symbol is a
1750
+ * stock (use `stocks.getQuote()` for those), and a `NotFoundError` when there is no
1751
+ * price data for the fund.
1752
+ */
1753
+ async quote(ticker) {
1754
+ return this.client.get(
1755
+ `/api/v1/etfs/${encodeURIComponent(ticker.toUpperCase())}/quote`
1756
+ );
1757
+ }
1729
1758
  /**
1730
1759
  * Get the full holdings composition for an ETF, including per-holding weights
1731
1760
  * and freshness metadata. Returns 404 for unknown ETFs or commodity-only funds.
@@ -2054,6 +2083,10 @@ var Institutional = class {
2054
2083
  * Resolved by URL slug (e.g. `Berkshire-Hathaway`) or numeric SEC CIK.
2055
2084
  * Free users receive the profile and top 10 holdings; PRO users receive the
2056
2085
  * full holdings array. Returns 404 if the slug or CIK is unknown.
2086
+ *
2087
+ * The signature remains unknown for compatibility. Opt in to the exported
2088
+ * PreviewResponse<InstitutionDetail> type to read data.positionsHeld, the full
2089
+ * portfolio count excluding SOLD_OUT rows, even on a truncated holdings list.
2057
2090
  */
2058
2091
  async getInstitutionDetail(slugOrCik) {
2059
2092
  return this.client.get(
@@ -2320,6 +2353,22 @@ var Stocks = class {
2320
2353
  async getEntities(ticker) {
2321
2354
  return this.client.get(`/api/v1/stocks/${encodeURIComponent(ticker)}/entities`);
2322
2355
  }
2356
+ /**
2357
+ * Get the company knowledge graph around a stock: the people, products, peers and other
2358
+ * entities linked to the company, and the typed relationships between them.
2359
+ *
2360
+ * Every identifier in the response is a slug, the same handle the metric and document
2361
+ * endpoints take, so a node can be passed straight to them. `depth` is 1 (default) or 2,
2362
+ * `cap` limits the number of non-root nodes (1 to 200, default 75); the root is always
2363
+ * included in addition. Out-of-range values are rejected with a 400. A ticker with no
2364
+ * graph returns a `NotFoundError`.
2365
+ */
2366
+ async getGraph(ticker, options) {
2367
+ return this.client.get(
2368
+ `/api/v1/stocks/${encodeURIComponent(ticker.toUpperCase())}/graph`,
2369
+ options
2370
+ );
2371
+ }
2323
2372
  /**
2324
2373
  * Get AI-generated stock analysis report. Requires PRO tier.
2325
2374
  *
@@ -2382,17 +2431,23 @@ var Stocks = class {
2382
2431
  async getHistoricalRevenue(ticker) {
2383
2432
  return this.client.get("/api/v1/stocks/fundamentals/historical/revenue", { ticker });
2384
2433
  }
2385
- /** Get short interest metrics (FINRA). */
2386
- async getShortInterest(ticker) {
2387
- return this.client.get("/api/v1/stocks/short-interest", { ticker });
2434
+ /**
2435
+ * Get short interest history (FINRA), newest first: shares short, days to cover and
2436
+ * average daily volume per settlement date. `limit` defaults to 24 settlement dates.
2437
+ */
2438
+ async getShortInterest(ticker, options) {
2439
+ return this.client.get("/api/v1/stocks/short-interest", { ticker, ...options });
2388
2440
  }
2389
2441
  /** Get float information. */
2390
2442
  async getFloat(ticker) {
2391
2443
  return this.client.get("/api/v1/stocks/float", { ticker });
2392
2444
  }
2393
- /** Get short volume trading data. */
2394
- async getShortVolume(ticker) {
2395
- return this.client.get("/api/v1/stocks/short-volume", { ticker });
2445
+ /**
2446
+ * Get daily short volume, newest first: shares sold short against total volume on the
2447
+ * reporting venues. `limit` defaults to 90 trading days.
2448
+ */
2449
+ async getShortVolume(ticker, options) {
2450
+ return this.client.get("/api/v1/stocks/short-volume", { ticker, ...options });
2396
2451
  }
2397
2452
  /**
2398
2453
  * Get company-specific KPI time-series for a ticker. Returns curated GAAP and
@@ -3641,6 +3696,37 @@ var optionsCommand = {
3641
3696
  };
3642
3697
 
3643
3698
  // src/cli/commands/quote.ts
3699
+ function valuation(row) {
3700
+ if (row.kind === "etf") {
3701
+ const etf = row.quote;
3702
+ return {
3703
+ kind: "facts",
3704
+ items: fields(
3705
+ field("AUM", humanize(etf.aum)),
3706
+ // Both ratios, like `dividendYield` on the stock quote.
3707
+ field("Expense ratio", ratioPercent(etf.expenseRatio)),
3708
+ field("NAV", fixed(etf.nav)),
3709
+ field("Div yield", ratioPercent(etf.dividendYield)),
3710
+ field("52w", `${fixed(etf.week52Low)} to ${fixed(etf.week52High)}`)
3711
+ )
3712
+ };
3713
+ }
3714
+ const quote = row.quote;
3715
+ return {
3716
+ kind: "facts",
3717
+ items: fields(
3718
+ field("Mkt cap", humanize(quote.marketCap)),
3719
+ field("P/E", fixed(quote.peRatio)),
3720
+ field("EPS TTM", fixed(quote.epsTTM)),
3721
+ // A ratio, unlike `changePercent` and the other percentages in the same payload.
3722
+ field("Div yield", ratioPercent(quote.dividendYield)),
3723
+ field(
3724
+ "52w",
3725
+ `${fixed(quote.week52Low)} to ${fixed(quote.week52High)}`
3726
+ )
3727
+ )
3728
+ };
3729
+ }
3644
3730
  function single(row, full) {
3645
3731
  const { quote, profile } = row;
3646
3732
  const tone = direction(quote.changePercent);
@@ -3668,20 +3754,7 @@ function single(row, full) {
3668
3754
  field("Prev close", fixed(quote.previousClose))
3669
3755
  )
3670
3756
  },
3671
- {
3672
- kind: "facts",
3673
- items: fields(
3674
- field("Mkt cap", humanize(quote.marketCap)),
3675
- field("P/E", fixed(quote.peRatio)),
3676
- field("EPS TTM", fixed(quote.epsTTM)),
3677
- // A ratio, unlike `changePercent` and the other percentages in the same payload.
3678
- field("Div yield", ratioPercent(quote.dividendYield)),
3679
- field(
3680
- "52w",
3681
- `${fixed(quote.week52Low)} to ${fixed(quote.week52High)}`
3682
- )
3683
- )
3684
- }
3757
+ valuation(row)
3685
3758
  ];
3686
3759
  if (quote.extendedHours) {
3687
3760
  const ext = quote.extendedHours;
@@ -3700,25 +3773,33 @@ function single(row, full) {
3700
3773
  )
3701
3774
  });
3702
3775
  }
3703
- if (full) {
3776
+ if (full && row.kind === "etf") {
3777
+ blocks.push({
3778
+ kind: "kv",
3779
+ items: fields(
3780
+ row.quote.inceptionDate ? field("inception", row.quote.inceptionDate) : void 0
3781
+ )
3782
+ });
3783
+ } else if (full && row.kind === "stock") {
3704
3784
  blocks.push({
3705
3785
  kind: "kv",
3706
3786
  items: fields(
3707
- field("200d average", fixed(quote.movingAverage200Day)),
3708
- quote.reportedCurrency ? field("reported currency", quote.reportedCurrency) : void 0,
3787
+ field("200d average", fixed(row.quote.movingAverage200Day)),
3788
+ row.quote.reportedCurrency ? field("reported currency", row.quote.reportedCurrency) : void 0,
3709
3789
  profile?.sector ? field("sector", String(profile.sector)) : void 0,
3710
3790
  profile?.industry ? field("industry", String(profile.industry)) : void 0,
3711
3791
  profile?.ceo ? field("ceo", String(profile.ceo)) : void 0
3712
3792
  )
3713
3793
  });
3714
3794
  }
3715
- if (quote.listingStatus === "DELISTED") {
3795
+ const listingStatus = row.kind === "stock" ? row.quote.listingStatus : void 0;
3796
+ if (row.kind === "stock" && listingStatus === "DELISTED") {
3716
3797
  blocks.push({
3717
3798
  kind: "text",
3718
- text: `Delisted on ${quote.delistedDate ?? "an unrecorded date"}. Every figure above is frozen at the last trade, not a live market move.`,
3799
+ text: `Delisted on ${row.quote.delistedDate ?? "an unrecorded date"}. Every figure above is frozen at the last trade, not a live market move.`,
3719
3800
  tone: "down"
3720
3801
  });
3721
- } else if (quote.listingStatus === "PENDING_DELISTING") {
3802
+ } else if (listingStatus === "PENDING_DELISTING") {
3722
3803
  blocks.push({
3723
3804
  kind: "text",
3724
3805
  text: "A merger or take-private is scheduled. The stock still trades, so these figures are current.",
@@ -3745,7 +3826,8 @@ function table(rows, full) {
3745
3826
  cell(signed(row.quote.change), tone),
3746
3827
  cell(signedPercent(row.quote.changePercent), tone),
3747
3828
  cell(humanize(row.quote.volume, 1)),
3748
- cell(humanize(row.quote.marketCap))
3829
+ // An ETF has no market cap; its AUM is a different number and stays out of this column.
3830
+ cell(humanize(row.kind === "stock" ? row.quote.marketCap : void 0))
3749
3831
  ];
3750
3832
  return full ? [cell(row.ticker), cell(row.profile?.name ?? ""), ...tail] : [cell(row.ticker), ...tail];
3751
3833
  })
@@ -3767,7 +3849,9 @@ var quoteCommand = {
3767
3849
  "Use canonical symbols, for example GOOGL rather than GOOG and BRK.B rather than BRK-B.",
3768
3850
  "One request per ticker. The company name costs a second request, so it is fetched only",
3769
3851
  "for the terminal layout: piped and --json output carry the quote alone.",
3770
- "JSON is the exact quote response for one ticker, and an object keyed by ticker for more."
3852
+ "JSON is the exact quote response for one ticker, and an object keyed by ticker for more.",
3853
+ "An ETF symbol answers with the ETF quote: AUM, expense ratio and NAV in place of market",
3854
+ "cap, P/E and EPS. That costs one extra request per ETF symbol."
3771
3855
  ],
3772
3856
  flags: {},
3773
3857
  async run({ args, client, full, mode }) {
@@ -3779,12 +3863,12 @@ var quoteCommand = {
3779
3863
  const wantName = mode === "pretty";
3780
3864
  const rows = await Promise.all(
3781
3865
  tickers.map(async (ticker) => {
3782
- const [quote, profile] = await Promise.all([
3783
- api.stocks.getQuote(ticker),
3866
+ const [answer, profile] = await Promise.all([
3867
+ quoteAnyTicker(api, ticker),
3784
3868
  // A name is a nicety, not the answer. A ticker with no profile still gets a quote.
3785
3869
  wantName ? api.stocks.getProfile(ticker).catch(() => null) : Promise.resolve(null)
3786
3870
  ]);
3787
- return { ticker, quote, profile };
3871
+ return { ...answer, ticker, profile };
3788
3872
  })
3789
3873
  );
3790
3874
  if (rows.length === 1) {
package/dist/index.cjs CHANGED
@@ -339,9 +339,11 @@ var Earnings = class {
339
339
  * management phrased it, and a summary of the earnings call.
340
340
  *
341
341
  * Branch on `isPreview`: a PRO key receives every hydrated quarter in full, a
342
- * FREE key receives the latest quarter shaped rather than truncated, plus
343
- * `totalCount`. {@link EarningsQuarter} documents which fields each tier
344
- * carries.
342
+ * FREE key receives the latest quarter shaped rather than truncated.
343
+ * `totalCount` on the envelope counts indexed quarters on both tiers. The
344
+ * returned array can be shorter when a body is unavailable or limit cuts it.
345
+ * Older responses may omit the count. {@link EarningsQuarter} documents
346
+ * which fields each tier carries.
345
347
  *
346
348
  * A quarter typically appears within 48 hours of the company reporting, and
347
349
  * the call summary can arrive after the press-release content for the same
@@ -475,6 +477,19 @@ var Etfs = class {
475
477
  async list() {
476
478
  return this.client.get("/api/v1/etfs");
477
479
  }
480
+ /**
481
+ * Get the quote snapshot for an ETF: price, day and 52-week range, volume, dividend
482
+ * yield, AUM, expense ratio, NAV and inception date.
483
+ *
484
+ * Fails with a 400 `APIError` whose `code` is `"ticker_is_not_etf"` when the symbol is a
485
+ * stock (use `stocks.getQuote()` for those), and a `NotFoundError` when there is no
486
+ * price data for the fund.
487
+ */
488
+ async quote(ticker) {
489
+ return this.client.get(
490
+ `/api/v1/etfs/${encodeURIComponent(ticker.toUpperCase())}/quote`
491
+ );
492
+ }
478
493
  /**
479
494
  * Get the full holdings composition for an ETF, including per-holding weights
480
495
  * and freshness metadata. Returns 404 for unknown ETFs or commodity-only funds.
@@ -803,6 +818,10 @@ var Institutional = class {
803
818
  * Resolved by URL slug (e.g. `Berkshire-Hathaway`) or numeric SEC CIK.
804
819
  * Free users receive the profile and top 10 holdings; PRO users receive the
805
820
  * full holdings array. Returns 404 if the slug or CIK is unknown.
821
+ *
822
+ * The signature remains unknown for compatibility. Opt in to the exported
823
+ * PreviewResponse<InstitutionDetail> type to read data.positionsHeld, the full
824
+ * portfolio count excluding SOLD_OUT rows, even on a truncated holdings list.
806
825
  */
807
826
  async getInstitutionDetail(slugOrCik) {
808
827
  return this.client.get(
@@ -1069,6 +1088,22 @@ var Stocks = class {
1069
1088
  async getEntities(ticker) {
1070
1089
  return this.client.get(`/api/v1/stocks/${encodeURIComponent(ticker)}/entities`);
1071
1090
  }
1091
+ /**
1092
+ * Get the company knowledge graph around a stock: the people, products, peers and other
1093
+ * entities linked to the company, and the typed relationships between them.
1094
+ *
1095
+ * Every identifier in the response is a slug, the same handle the metric and document
1096
+ * endpoints take, so a node can be passed straight to them. `depth` is 1 (default) or 2,
1097
+ * `cap` limits the number of non-root nodes (1 to 200, default 75); the root is always
1098
+ * included in addition. Out-of-range values are rejected with a 400. A ticker with no
1099
+ * graph returns a `NotFoundError`.
1100
+ */
1101
+ async getGraph(ticker, options) {
1102
+ return this.client.get(
1103
+ `/api/v1/stocks/${encodeURIComponent(ticker.toUpperCase())}/graph`,
1104
+ options
1105
+ );
1106
+ }
1072
1107
  /**
1073
1108
  * Get AI-generated stock analysis report. Requires PRO tier.
1074
1109
  *
@@ -1131,17 +1166,23 @@ var Stocks = class {
1131
1166
  async getHistoricalRevenue(ticker) {
1132
1167
  return this.client.get("/api/v1/stocks/fundamentals/historical/revenue", { ticker });
1133
1168
  }
1134
- /** Get short interest metrics (FINRA). */
1135
- async getShortInterest(ticker) {
1136
- return this.client.get("/api/v1/stocks/short-interest", { ticker });
1169
+ /**
1170
+ * Get short interest history (FINRA), newest first: shares short, days to cover and
1171
+ * average daily volume per settlement date. `limit` defaults to 24 settlement dates.
1172
+ */
1173
+ async getShortInterest(ticker, options) {
1174
+ return this.client.get("/api/v1/stocks/short-interest", { ticker, ...options });
1137
1175
  }
1138
1176
  /** Get float information. */
1139
1177
  async getFloat(ticker) {
1140
1178
  return this.client.get("/api/v1/stocks/float", { ticker });
1141
1179
  }
1142
- /** Get short volume trading data. */
1143
- async getShortVolume(ticker) {
1144
- return this.client.get("/api/v1/stocks/short-volume", { ticker });
1180
+ /**
1181
+ * Get daily short volume, newest first: shares sold short against total volume on the
1182
+ * reporting venues. `limit` defaults to 90 trading days.
1183
+ */
1184
+ async getShortVolume(ticker, options) {
1185
+ return this.client.get("/api/v1/stocks/short-volume", { ticker, ...options });
1145
1186
  }
1146
1187
  /**
1147
1188
  * Get company-specific KPI time-series for a ticker. Returns curated GAAP and
@@ -1353,7 +1394,7 @@ var Trackers = class {
1353
1394
  };
1354
1395
 
1355
1396
  // src/version.ts
1356
- var VERSION = "0.61.0";
1397
+ var VERSION = "0.63.0";
1357
1398
 
1358
1399
  // src/client.ts
1359
1400
  var DEFAULT_BASE_URL = "https://app.sentisense.ai";