sentisense 0.50.0 → 0.52.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
@@ -71,7 +71,8 @@ npx -y sentisense@latest health
71
71
  | `quote <ticker>...` | Price, day range, 52-week range, market cap, P/E. One request per ticker |
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
- | `analysts <ticker>` | Consensus, price target band, recent upgrades and downgrades |
74
+ | `analysts <ticker>` | Consensus, price target band, recent upgrades and downgrades. `--coverage` for who covers it, by firm |
75
+ | `analyst <slug>` | One analyst: their firms, their coverage book, and `--calls` for their price target notes |
75
76
  | `earnings [ticker]` | Forward calendar with no ticker, per-quarter analysis with one (`earnings AAPL`) |
76
77
  | `insiders <ticker>` | Filed Form 4 transactions, including whether they were pre-planned |
77
78
  | `insights <ticker>` | Generated signals, filterable by `--urgency` and `--type` |
@@ -80,9 +81,22 @@ npx -y sentisense@latest health
80
81
  | `flows [ticker]` | Institutional 13F flows, or one ticker's holders and notable changes |
81
82
  | `options <ticker>` | End-of-day options positioning, IV rank, walls, unusual contracts |
82
83
  | `screen --filter ...` | Screen the universe on Score, analyst, technical, and price fields |
84
+ | `search <name>` | Resolve a name, alias, ticker or slug to a symbol and the entity handle |
83
85
 
84
86
  Run `sentisense help <command>` for its flags and examples.
85
87
 
88
+ Three of these chain into each other. Start from a name, land on a person:
89
+
90
+ ```bash
91
+ npx -y sentisense@latest search Tesla --type company # "Tesla" -> TSLA, plus the entity slug
92
+ npx -y sentisense@latest analysts NVDA --coverage # who covers it, by firm, with analyst slugs
93
+ npx -y sentisense@latest analyst quinn-bolton --calls # that analyst's firms, book, and notes
94
+ ```
95
+
96
+ `analyst` takes a slug, not a name: slugs are lowercase and hyphenated, and every named analyst in a `--coverage` row carries the one that addresses them. A name is rejected before a request is spent. What comes back is call history, not accuracy scoring: there is no hit rate, no ranking, and nothing in it rates the person.
97
+
98
+ Ranking on `search` is the API's own, and a company can sort below its own products, so pass `--type company` when what you want is the issuer.
99
+
86
100
  ### Output modes
87
101
 
88
102
  Readable in a terminal, plain text when piped, and exact API JSON on request:
@@ -212,7 +226,7 @@ client.stocks.getFundamentals("AAPL") // Financial data
212
226
  client.stocks.getShortInterest("GME") // Short interest
213
227
  client.stocks.getOptionsSummary("NVDA") // End-of-day options dossier
214
228
  client.stocks.getOptionsHistory("NVDA", { window: "2y" }) // Daily options aggregates over time
215
- client.stocks.getRating("AAPL") // SentiSense Rating: letter, percentile, dimensions
229
+ client.stocks.getRating("AAPL") // SentiSense Rating: score, letter, percentile, dimensions
216
230
  client.stocks.getAISummary("AAPL", { depth: "deep" }) // AI report (PRO)
217
231
  ```
218
232
 
@@ -426,7 +440,7 @@ client.entityMetrics.getDistribution("AAPL", "sentiment")
426
440
  client.entityMetrics.getDistribution("AAPL", "mentions", { dimension: "source" })
427
441
  ```
428
442
 
429
- Available metric types: `mentions`, `sentiment`, `sentisense_score`, `sentisense_rating`, `social_dominance`, `creators`. `sentisense_rating` is the SentiSense Rating percentile and is a time series only: it has no source breakdown, so `getDistribution` answers with an empty distribution for it.
443
+ Available metric types: `mentions`, `sentiment`, `sentisense_score`, `sentisense_rating`, `social_dominance`, `creators`. `sentisense_rating` is the SentiSense Rating score and is a time series only: it has no source breakdown, so `getDistribution` answers with an empty distribution for it.
430
444
 
431
445
  ### Options
432
446
 
@@ -444,12 +458,13 @@ A row whose baseline is still building carries its raw readings with the percent
444
458
 
445
459
  ### SentiSense Rating
446
460
 
447
- Where a stock ranks against the other stocks rated that day, as a letter and a percentile, plus the six 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).
461
+ Where a stock ranks against the other stocks rated that day, as a score, a letter and a percentile, plus the six 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).
448
462
 
449
463
  ```typescript
450
464
  const rating = await client.stocks.getRating("AAPL");
451
465
  if (rating.rated) {
452
- console.log(rating.letter, rating.percentile, "of", rating.ratedCount, "rated stocks");
466
+ console.log(rating.letter, rating.score, "percentile", rating.percentile, "of", rating.ratedCount);
467
+ for (const adj of rating.riskAdjustments ?? []) console.log(" ", adj.condition, -adj.points);
453
468
  for (const dim of rating.dimensions.filter((d) => d.present)) {
454
469
  console.log(" ", dim.label, dim.percentile);
455
470
  }
@@ -458,19 +473,33 @@ if (rating.rated) {
458
473
  }
459
474
  ```
460
475
 
461
- `StockRating` is a discriminated union on `rated`, so the `if` narrows to `letter`, `percentile`, `composite`, `ratedCount` and `methodologyVersion`, and the `else` narrows to `reason`, `dimensionsPresent` and `presentDimensions`. Branch on that flag rather than testing a field for `undefined`.
476
+ `getRating` returns `StockRatingResponse`, a discriminated union on `rated` of `StockRating` (graded) and `StockNotRated`. The `if` narrows to `score`, `letter`, `percentile`, `composite`, `ratedCount` and `methodologyVersion`, the `else` to `reason`, `dimensionsPresent` and `presentDimensions`. Branch on that flag rather than testing a field for `undefined`.
462
477
 
463
478
  Having no grade is a normal 200, not a 404: ETFs and tickers outside the swept universe answer that way, and `reason` is one of `stale`, `not_rated_today`, `insufficient_dimensions` or `insufficient_coverage_weight`. Only a ticker that resolves to nothing we track rejects with `NotFoundError`.
464
479
 
465
- `dimensions` always holds all six rows in a fixed order, including the ones with no data, which arrive with `present` false and a `null` percentile. Read `present` first and never substitute zero for a missing percentile: zero is the bottom of the cross-section, absence is not a position on it. Only the smart-money dimension carries `subLegs`. `letter` is served as stored rather than derived from `percentile`, so read it instead of computing your own bucket edges. For the daily history of a stock's percentile, ask `entityMetrics.getMetrics` for the `sentisense_rating` metric.
480
+ `dimensions` always holds all six rows in a fixed order, including the ones with no data, which arrive with `present` false and a `null` percentile. Read `present` first and never substitute zero for a missing percentile: zero is the bottom of the cross-section, absence is not a position on it. Only the smart-money dimension carries `subLegs`.
481
+
482
+ **`score` is not `percentile`.** `percentile` is the rank of the blended signals against the day's rated set. `score = percentile - sum(riskAdjustments.map((a) => a.points))`, floored at 10 when fewer than five dimensions are available, and it is the number `letter` bands (A 90, B 70, C 30, D 10). `bucketLetter` is the band the percentile alone would give, so the two letters differ by exactly what the conditions cost. `riskAdjustments` itemises that cost, `penaltyPoints` totals it, and `riskConditions` names the active ones from the `RiskCondition` union: `thin_coverage`, `weak_dimension`, `unprofitable`, `no_fundamentals`, `high_leverage`, `unseasoned_listing`, `small_market_cap`, `thin_liquidity`, `extended_price`, `insider_selling` and `institutional_outflow`.
483
+
484
+ All five are optional: a response served before they shipped omits them. For the daily history of a stock's score, ask `entityMetrics.getMetrics` for the `sentisense_rating` metric.
466
485
 
467
486
  ### Market mood & knowledge base
468
487
 
469
488
  ```typescript
470
489
  client.marketMood.get() // Composite market sentiment with sub-signals
471
490
  client.kb.getPopularEntities() // Most-tracked entities
491
+ client.kb.searchEntities("Tesla") // Resolve a name, alias, ticker or slug to what we track
492
+ ```
493
+
494
+ Entity search is resolution, not enumeration: the query must be at least 2 characters, the match count is capped at 25, and it returns a bare `EntitySearchResult[]` rather than a `PreviewResponse` envelope. Each hit carries `name`, `type`, the `ticker` for a listed entity (`null` for everything else), and the `urlSlug` the metric endpoints address that entity by, which is the only way to get a handle for a person, product or topic with no ticker.
495
+
496
+ ```typescript
497
+ const hits = await client.kb.searchEntities("Tesla", { type: "company", limit: 5 });
498
+ const symbol = hits.find((hit) => hit.ticker)?.ticker; // "TSLA"
472
499
  ```
473
500
 
501
+ An empty array is the normal answer for a query that matches nothing, so branch on `length` rather than catching.
502
+
474
503
  ### Screener
475
504
 
476
505
  Filter the tracked universe on the SentiSense Score, attention, analyst consensus, technicals and price in one query. Screening on analyst ratings alone is something a dozen free tools do; screening on analyst ratings *where the Score disagrees* is not.