sentisense 0.47.2 → 0.50.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 +65 -2
- package/dist/cli.cjs +116 -1
- package/dist/index.cjs +116 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +407 -3
- package/dist/index.d.ts +407 -3
- package/dist/index.mjs +116 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/sentisense)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
Official JavaScript/TypeScript SDK and CLI for the [SentiSense](https://sentisense.ai) market intelligence API: stock prices, news and social sentiment, the SentiSense Score, insider and congressional trading, institutional 13F flows, options positioning, analyst ratings, earnings analysis, and a cross-signal screener.
|
|
6
|
+
Official JavaScript/TypeScript SDK and CLI for the [SentiSense](https://sentisense.ai) market intelligence API: stock prices, news and social sentiment, the SentiSense Score, the SentiSense Rating, insider and congressional trading, institutional 13F flows, options positioning, analyst ratings, earnings analysis, and a cross-signal screener.
|
|
7
7
|
|
|
8
8
|
- Full TypeScript support with detailed type definitions
|
|
9
9
|
- Works in Node.js 18+, Deno, Bun, and browsers
|
|
@@ -212,6 +212,7 @@ client.stocks.getFundamentals("AAPL") // Financial data
|
|
|
212
212
|
client.stocks.getShortInterest("GME") // Short interest
|
|
213
213
|
client.stocks.getOptionsSummary("NVDA") // End-of-day options dossier
|
|
214
214
|
client.stocks.getOptionsHistory("NVDA", { window: "2y" }) // Daily options aggregates over time
|
|
215
|
+
client.stocks.getRating("AAPL") // SentiSense Rating: letter, percentile, dimensions
|
|
215
216
|
client.stocks.getAISummary("AAPL", { depth: "deep" }) // AI report (PRO)
|
|
216
217
|
```
|
|
217
218
|
|
|
@@ -318,6 +319,46 @@ client.analyst.estimates("AAPL") // Forward EPS + surpris
|
|
|
318
319
|
client.analyst.marketActivity({ lookbackDays: 7 }) // Market-wide analyst actions (PRO).
|
|
319
320
|
```
|
|
320
321
|
|
|
322
|
+
Coverage answers "who covers this stock and what did they say" in one call, and it is the entry point into the per-analyst surfaces: every named analyst carries the slug that addresses their profile and their calls.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
client.analyst.coverage("NVDA", { lookbackDays: 365 }) // Who covers it, by firm. Free: 5 firms.
|
|
326
|
+
client.analyst.profile("gil-luria") // One analyst's firms + coverage book.
|
|
327
|
+
client.analyst.calls("gil-luria", { limit: 25 }) // Their price target notes, newest first.
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
const { data: book } = await client.analyst.coverage("NVDA");
|
|
332
|
+
|
|
333
|
+
console.log(`${book.firmCount} firms, ${book.namedAnalystCount} named analysts`);
|
|
334
|
+
console.log(`${book.attributedNoteCount} of ${book.noteCount} notes name someone`);
|
|
335
|
+
|
|
336
|
+
const buckets = book.ratingBuckets;
|
|
337
|
+
if (buckets) {
|
|
338
|
+
console.log(`${buckets.buy} buy, ${buckets.hold} hold, ${buckets.sell} sell, ${buckets.unrated} unrated of ${buckets.total}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
for (const row of book.coverage.slice(0, 5)) {
|
|
342
|
+
if (row.noteCount === 0) {
|
|
343
|
+
// A desk can cover a stock on rating actions alone, with no price target.
|
|
344
|
+
console.log(`${row.firm}: rating only, ${row.firmRating?.rating}`);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const who = row.latestNote?.analyst ?? "unattributed";
|
|
348
|
+
console.log(`${row.firm}: ${row.latestNote?.priceTarget} (${who})`);
|
|
349
|
+
|
|
350
|
+
for (const analyst of row.analysts) {
|
|
351
|
+
if (!analyst.slug) continue;
|
|
352
|
+
const calls = await client.analyst.calls(analyst.slug, { limit: 10 });
|
|
353
|
+
console.log(` ${analyst.name}: ${calls.totalCount} notes on record`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Two shapes to read rather than assume. A firm can appear with `noteCount: 0`, a `null` `latestNote` and a populated `firmRating`, because coverage means a price target **or** a rating action in the window. And a large, publisher-dependent share of notes name no individual, so an empty `analysts` array alongside a non-zero `noteCount` is normal: read `attributedNoteCount` and `unattributedNoteCount` off the response rather than hardcoding a rate. The response-level counts survive the free truncation, so they describe the whole window even when only 5 rows come back. An unknown slug throws `NotFoundError`, which keeps "published nothing we hold" distinguishable from "does not exist".
|
|
359
|
+
|
|
360
|
+
`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.
|
|
361
|
+
|
|
321
362
|
### Earnings
|
|
322
363
|
|
|
323
364
|
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. Both return the preview envelope.
|
|
@@ -385,7 +426,7 @@ client.entityMetrics.getDistribution("AAPL", "sentiment")
|
|
|
385
426
|
client.entityMetrics.getDistribution("AAPL", "mentions", { dimension: "source" })
|
|
386
427
|
```
|
|
387
428
|
|
|
388
|
-
Available metric types: `mentions`, `sentiment`, `
|
|
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.
|
|
389
430
|
|
|
390
431
|
### Options
|
|
391
432
|
|
|
@@ -401,6 +442,28 @@ The radar carries two separately-ranked boards: `data.rows` for stocks and `data
|
|
|
401
442
|
|
|
402
443
|
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; `getOptionsHistory` reports it as an empty `series` instead, so check the array rather than null-checking there.
|
|
403
444
|
|
|
445
|
+
### SentiSense Rating
|
|
446
|
+
|
|
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).
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
const rating = await client.stocks.getRating("AAPL");
|
|
451
|
+
if (rating.rated) {
|
|
452
|
+
console.log(rating.letter, rating.percentile, "of", rating.ratedCount, "rated stocks");
|
|
453
|
+
for (const dim of rating.dimensions.filter((d) => d.present)) {
|
|
454
|
+
console.log(" ", dim.label, dim.percentile);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
console.log("no grade today:", rating.reason);
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
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`.
|
|
462
|
+
|
|
463
|
+
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
|
+
|
|
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.
|
|
466
|
+
|
|
404
467
|
### Market mood & knowledge base
|
|
405
468
|
|
|
406
469
|
```typescript
|
package/dist/cli.cjs
CHANGED
|
@@ -1037,7 +1037,7 @@ var flowsCommand = {
|
|
|
1037
1037
|
};
|
|
1038
1038
|
|
|
1039
1039
|
// src/version.ts
|
|
1040
|
-
var VERSION = "0.
|
|
1040
|
+
var VERSION = "0.50.0";
|
|
1041
1041
|
|
|
1042
1042
|
// src/resources/analyst.ts
|
|
1043
1043
|
var Analyst = class {
|
|
@@ -1079,6 +1079,89 @@ var Analyst = class {
|
|
|
1079
1079
|
async marketActivity(options) {
|
|
1080
1080
|
return this.client.get("/api/v1/analyst/activity", options);
|
|
1081
1081
|
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Get who covers a ticker and what they most recently said, grouped by firm, most
|
|
1084
|
+
* recently active firm first.
|
|
1085
|
+
*
|
|
1086
|
+
* This is the one-call answer to "who covers AMD and what do they say". Each row in
|
|
1087
|
+
* `data.coverage` is a firm, the individual analysts we can name on that firm's desk,
|
|
1088
|
+
* that firm's most recent price target note, and that firm's most recent rating action.
|
|
1089
|
+
*
|
|
1090
|
+
* A PRO key receives every firm. A FREE key receives the 5 most recently active firms
|
|
1091
|
+
* with every response-level count intact, so the counts describe the full window even
|
|
1092
|
+
* when the rows do not.
|
|
1093
|
+
*
|
|
1094
|
+
* Two shapes to read rather than assume. **A firm can cover a stock without publishing
|
|
1095
|
+
* a price target**, because coverage means a note or a rating action in the window: that
|
|
1096
|
+
* row carries `noteCount: 0`, a `null` `latestNote` and a populated `firmRating`, so
|
|
1097
|
+
* read `noteCount` on the row instead of expecting a note. And **not every note names
|
|
1098
|
+
* its analyst**, at a rate that is a property of the publisher and varies enormously by
|
|
1099
|
+
* ticker, so a firm can appear with an empty `analysts` array and a non-zero
|
|
1100
|
+
* `noteCount`, and `latestNote.analyst` can be `null`. Read `attributedNoteCount` and
|
|
1101
|
+
* `unattributedNoteCount` off the response you received rather than hardcoding a rate.
|
|
1102
|
+
*
|
|
1103
|
+
* `firmRating` belongs to the firm, not to a person: rating actions are published at
|
|
1104
|
+
* firm level with no individual attached.
|
|
1105
|
+
*
|
|
1106
|
+
* Each named analyst carries the `slug` that addresses {@link profile} and
|
|
1107
|
+
* {@link calls}, so a coverage response is the natural entry point into a person.
|
|
1108
|
+
*/
|
|
1109
|
+
async coverage(ticker, options) {
|
|
1110
|
+
return this.client.get(
|
|
1111
|
+
`/api/v1/analyst/${encodeURIComponent(ticker.toUpperCase())}/coverage`,
|
|
1112
|
+
options
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Get one analyst: the firms they have published under, the window of notes we hold at
|
|
1117
|
+
* each, and the tickers they cover. Throws `NotFoundError` when the slug matches no
|
|
1118
|
+
* analyst.
|
|
1119
|
+
*
|
|
1120
|
+
* A PRO key receives the full book. A FREE key receives the profile with
|
|
1121
|
+
* `data.coverage` truncated to the 5 most recently covered tickers, and the envelope's
|
|
1122
|
+
* `totalCount` reporting how many there are in full.
|
|
1123
|
+
*
|
|
1124
|
+
* `firstSeen` and `lastSeen` are observation windows, not employment dates: they bound
|
|
1125
|
+
* the notes we hold from that analyst at that firm. `mostRecentFirm` says where they
|
|
1126
|
+
* last published, not where they work today. Do not render either as a hire or
|
|
1127
|
+
* departure date.
|
|
1128
|
+
*
|
|
1129
|
+
* This is call history, not a scorecard. There is no accuracy score, hit rate or
|
|
1130
|
+
* ranking here, and nothing in the response should be read as a rating of the person.
|
|
1131
|
+
*
|
|
1132
|
+
* @param slug Analyst slug, lowercased and hyphenated (e.g. `"dan-ives"`). You do not
|
|
1133
|
+
* have to guess one: every named analyst in a {@link coverage} response carries it.
|
|
1134
|
+
*/
|
|
1135
|
+
async profile(slug) {
|
|
1136
|
+
return this.client.get(
|
|
1137
|
+
`/api/v1/analyst/people/${encodeURIComponent(slug)}`
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Get one analyst's price target notes, newest first, paged. Throws `NotFoundError`
|
|
1142
|
+
* when the slug matches no analyst, which keeps "this analyst has published nothing we
|
|
1143
|
+
* hold" (an empty page) distinguishable from "this analyst does not exist".
|
|
1144
|
+
*
|
|
1145
|
+
* Ordered by published date descending with the row id as the final tie-break, a total
|
|
1146
|
+
* order, so walking the history with `offset` never drops or repeats a row. That
|
|
1147
|
+
* matters more than it looks: a single roundup article carries several of one analyst's
|
|
1148
|
+
* notes at an identical timestamp.
|
|
1149
|
+
*
|
|
1150
|
+
* A FREE key receives the first 25 rows as a complete response (`isPreview: false`);
|
|
1151
|
+
* asking for a larger `limit` or an `offset` past row 25 returns the free in-allowance
|
|
1152
|
+
* slice with `previewReason: "PRO_REQUIRED"`. A PRO key pages the whole history. The
|
|
1153
|
+
* envelope's `totalCount` is the analyst's whole attributed history rather than the page
|
|
1154
|
+
* size, so `offset + data.length < totalCount` tells you another page is available.
|
|
1155
|
+
*
|
|
1156
|
+
* Dates are day granularity on purpose. Publisher timestamps are not comparable across
|
|
1157
|
+
* sources, so a time of day would advertise precision the data does not have.
|
|
1158
|
+
*/
|
|
1159
|
+
async calls(slug, options) {
|
|
1160
|
+
return this.client.get(
|
|
1161
|
+
`/api/v1/analyst/people/${encodeURIComponent(slug)}/calls`,
|
|
1162
|
+
options
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1082
1165
|
};
|
|
1083
1166
|
|
|
1084
1167
|
// src/resources/calendar.ts
|
|
@@ -1986,6 +2069,38 @@ var Stocks = class {
|
|
|
1986
2069
|
options
|
|
1987
2070
|
);
|
|
1988
2071
|
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Get the SentiSense Rating for one stock: where it ranks against the other stocks rated
|
|
2074
|
+
* that day, and the six dimensions the rank is blended from.
|
|
2075
|
+
*
|
|
2076
|
+
* The Rating is a *relative*, automatically generated research signal, for informational
|
|
2077
|
+
* and educational purposes only. It ranks a stock against its cross-section; it is not
|
|
2078
|
+
* financial, investment or trading advice and it is not a recommendation about any
|
|
2079
|
+
* security. `disclaimer` carries the wording to display alongside a grade. Methodology:
|
|
2080
|
+
* https://sentisense.ai/methodology/#sentisense-rating
|
|
2081
|
+
*
|
|
2082
|
+
* **A discriminated union on `rated`.** `if (rating.rated)` narrows to `letter`,
|
|
2083
|
+
* `percentile`, `composite`, `ratedCount` and `methodologyVersion`; the `else` branch
|
|
2084
|
+
* narrows to `reason`, `dimensionsPresent` and `presentDimensions`. Branch on the flag
|
|
2085
|
+
* rather than testing a field for `undefined`.
|
|
2086
|
+
*
|
|
2087
|
+
* **Having no grade is a normal 200, not a 404.** ETFs and tickers outside the swept
|
|
2088
|
+
* universe answer with `rated` false, and the composition still arrives so a card can
|
|
2089
|
+
* render. Only a ticker that resolves to nothing we track rejects with
|
|
2090
|
+
* {@link NotFoundError}; a request with no usable key rejects with
|
|
2091
|
+
* {@link AuthenticationError}.
|
|
2092
|
+
*
|
|
2093
|
+
* `dimensions` always holds all six rows in a fixed order, including the ones with no
|
|
2094
|
+
* data, which arrive with `present` false and a `null` percentile. Read `present` first
|
|
2095
|
+
* and never read a missing percentile as zero. `letter` is served as stored rather than
|
|
2096
|
+
* derived from `percentile`, so read it instead of computing your own bucket edges.
|
|
2097
|
+
*
|
|
2098
|
+
* For the daily history of a stock's percentile, ask `client.entityMetrics.getMetrics`
|
|
2099
|
+
* for the `sentisense_rating` metric.
|
|
2100
|
+
*/
|
|
2101
|
+
async getRating(ticker) {
|
|
2102
|
+
return this.client.get(`/api/v1/rating/${encodeURIComponent(ticker.toUpperCase())}`);
|
|
2103
|
+
}
|
|
1989
2104
|
};
|
|
1990
2105
|
|
|
1991
2106
|
// src/resources/indexes.ts
|
package/dist/index.cjs
CHANGED
|
@@ -114,6 +114,89 @@ var Analyst = class {
|
|
|
114
114
|
async marketActivity(options) {
|
|
115
115
|
return this.client.get("/api/v1/analyst/activity", options);
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Get who covers a ticker and what they most recently said, grouped by firm, most
|
|
119
|
+
* recently active firm first.
|
|
120
|
+
*
|
|
121
|
+
* This is the one-call answer to "who covers AMD and what do they say". Each row in
|
|
122
|
+
* `data.coverage` is a firm, the individual analysts we can name on that firm's desk,
|
|
123
|
+
* that firm's most recent price target note, and that firm's most recent rating action.
|
|
124
|
+
*
|
|
125
|
+
* A PRO key receives every firm. A FREE key receives the 5 most recently active firms
|
|
126
|
+
* with every response-level count intact, so the counts describe the full window even
|
|
127
|
+
* when the rows do not.
|
|
128
|
+
*
|
|
129
|
+
* Two shapes to read rather than assume. **A firm can cover a stock without publishing
|
|
130
|
+
* a price target**, because coverage means a note or a rating action in the window: that
|
|
131
|
+
* row carries `noteCount: 0`, a `null` `latestNote` and a populated `firmRating`, so
|
|
132
|
+
* read `noteCount` on the row instead of expecting a note. And **not every note names
|
|
133
|
+
* its analyst**, at a rate that is a property of the publisher and varies enormously by
|
|
134
|
+
* ticker, so a firm can appear with an empty `analysts` array and a non-zero
|
|
135
|
+
* `noteCount`, and `latestNote.analyst` can be `null`. Read `attributedNoteCount` and
|
|
136
|
+
* `unattributedNoteCount` off the response you received rather than hardcoding a rate.
|
|
137
|
+
*
|
|
138
|
+
* `firmRating` belongs to the firm, not to a person: rating actions are published at
|
|
139
|
+
* firm level with no individual attached.
|
|
140
|
+
*
|
|
141
|
+
* Each named analyst carries the `slug` that addresses {@link profile} and
|
|
142
|
+
* {@link calls}, so a coverage response is the natural entry point into a person.
|
|
143
|
+
*/
|
|
144
|
+
async coverage(ticker, options) {
|
|
145
|
+
return this.client.get(
|
|
146
|
+
`/api/v1/analyst/${encodeURIComponent(ticker.toUpperCase())}/coverage`,
|
|
147
|
+
options
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get one analyst: the firms they have published under, the window of notes we hold at
|
|
152
|
+
* each, and the tickers they cover. Throws `NotFoundError` when the slug matches no
|
|
153
|
+
* analyst.
|
|
154
|
+
*
|
|
155
|
+
* A PRO key receives the full book. A FREE key receives the profile with
|
|
156
|
+
* `data.coverage` truncated to the 5 most recently covered tickers, and the envelope's
|
|
157
|
+
* `totalCount` reporting how many there are in full.
|
|
158
|
+
*
|
|
159
|
+
* `firstSeen` and `lastSeen` are observation windows, not employment dates: they bound
|
|
160
|
+
* the notes we hold from that analyst at that firm. `mostRecentFirm` says where they
|
|
161
|
+
* last published, not where they work today. Do not render either as a hire or
|
|
162
|
+
* departure date.
|
|
163
|
+
*
|
|
164
|
+
* This is call history, not a scorecard. There is no accuracy score, hit rate or
|
|
165
|
+
* ranking here, and nothing in the response should be read as a rating of the person.
|
|
166
|
+
*
|
|
167
|
+
* @param slug Analyst slug, lowercased and hyphenated (e.g. `"dan-ives"`). You do not
|
|
168
|
+
* have to guess one: every named analyst in a {@link coverage} response carries it.
|
|
169
|
+
*/
|
|
170
|
+
async profile(slug) {
|
|
171
|
+
return this.client.get(
|
|
172
|
+
`/api/v1/analyst/people/${encodeURIComponent(slug)}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get one analyst's price target notes, newest first, paged. Throws `NotFoundError`
|
|
177
|
+
* when the slug matches no analyst, which keeps "this analyst has published nothing we
|
|
178
|
+
* hold" (an empty page) distinguishable from "this analyst does not exist".
|
|
179
|
+
*
|
|
180
|
+
* Ordered by published date descending with the row id as the final tie-break, a total
|
|
181
|
+
* order, so walking the history with `offset` never drops or repeats a row. That
|
|
182
|
+
* matters more than it looks: a single roundup article carries several of one analyst's
|
|
183
|
+
* notes at an identical timestamp.
|
|
184
|
+
*
|
|
185
|
+
* A FREE key receives the first 25 rows as a complete response (`isPreview: false`);
|
|
186
|
+
* asking for a larger `limit` or an `offset` past row 25 returns the free in-allowance
|
|
187
|
+
* slice with `previewReason: "PRO_REQUIRED"`. A PRO key pages the whole history. The
|
|
188
|
+
* envelope's `totalCount` is the analyst's whole attributed history rather than the page
|
|
189
|
+
* size, so `offset + data.length < totalCount` tells you another page is available.
|
|
190
|
+
*
|
|
191
|
+
* Dates are day granularity on purpose. Publisher timestamps are not comparable across
|
|
192
|
+
* sources, so a time of day would advertise precision the data does not have.
|
|
193
|
+
*/
|
|
194
|
+
async calls(slug, options) {
|
|
195
|
+
return this.client.get(
|
|
196
|
+
`/api/v1/analyst/people/${encodeURIComponent(slug)}/calls`,
|
|
197
|
+
options
|
|
198
|
+
);
|
|
199
|
+
}
|
|
117
200
|
};
|
|
118
201
|
|
|
119
202
|
// src/resources/calendar.ts
|
|
@@ -1021,6 +1104,38 @@ var Stocks = class {
|
|
|
1021
1104
|
options
|
|
1022
1105
|
);
|
|
1023
1106
|
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Get the SentiSense Rating for one stock: where it ranks against the other stocks rated
|
|
1109
|
+
* that day, and the six dimensions the rank is blended from.
|
|
1110
|
+
*
|
|
1111
|
+
* The Rating is a *relative*, automatically generated research signal, for informational
|
|
1112
|
+
* and educational purposes only. It ranks a stock against its cross-section; it is not
|
|
1113
|
+
* financial, investment or trading advice and it is not a recommendation about any
|
|
1114
|
+
* security. `disclaimer` carries the wording to display alongside a grade. Methodology:
|
|
1115
|
+
* https://sentisense.ai/methodology/#sentisense-rating
|
|
1116
|
+
*
|
|
1117
|
+
* **A discriminated union on `rated`.** `if (rating.rated)` narrows to `letter`,
|
|
1118
|
+
* `percentile`, `composite`, `ratedCount` and `methodologyVersion`; the `else` branch
|
|
1119
|
+
* narrows to `reason`, `dimensionsPresent` and `presentDimensions`. Branch on the flag
|
|
1120
|
+
* rather than testing a field for `undefined`.
|
|
1121
|
+
*
|
|
1122
|
+
* **Having no grade is a normal 200, not a 404.** ETFs and tickers outside the swept
|
|
1123
|
+
* universe answer with `rated` false, and the composition still arrives so a card can
|
|
1124
|
+
* render. Only a ticker that resolves to nothing we track rejects with
|
|
1125
|
+
* {@link NotFoundError}; a request with no usable key rejects with
|
|
1126
|
+
* {@link AuthenticationError}.
|
|
1127
|
+
*
|
|
1128
|
+
* `dimensions` always holds all six rows in a fixed order, including the ones with no
|
|
1129
|
+
* data, which arrive with `present` false and a `null` percentile. Read `present` first
|
|
1130
|
+
* and never read a missing percentile as zero. `letter` is served as stored rather than
|
|
1131
|
+
* derived from `percentile`, so read it instead of computing your own bucket edges.
|
|
1132
|
+
*
|
|
1133
|
+
* For the daily history of a stock's percentile, ask `client.entityMetrics.getMetrics`
|
|
1134
|
+
* for the `sentisense_rating` metric.
|
|
1135
|
+
*/
|
|
1136
|
+
async getRating(ticker) {
|
|
1137
|
+
return this.client.get(`/api/v1/rating/${encodeURIComponent(ticker.toUpperCase())}`);
|
|
1138
|
+
}
|
|
1024
1139
|
};
|
|
1025
1140
|
|
|
1026
1141
|
// src/resources/indexes.ts
|
|
@@ -1100,7 +1215,7 @@ var Trackers = class {
|
|
|
1100
1215
|
};
|
|
1101
1216
|
|
|
1102
1217
|
// src/version.ts
|
|
1103
|
-
var VERSION = "0.
|
|
1218
|
+
var VERSION = "0.50.0";
|
|
1104
1219
|
|
|
1105
1220
|
// src/client.ts
|
|
1106
1221
|
var DEFAULT_BASE_URL = "https://app.sentisense.ai";
|