aiden-shared-calculations-unified 1.0.83 → 1.0.86
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/calculations/core/asset-pnl-status.js +122 -104
- package/calculations/core/asset-position-size.js +110 -73
- package/calculations/core/average-daily-pnl-all-users.js +17 -3
- package/calculations/core/average-daily-pnl-per-sector.js +83 -75
- package/calculations/core/average-daily-pnl-per-stock.js +84 -73
- package/calculations/core/average-daily-position-pnl.js +2 -2
- package/calculations/core/holding-duration-per-asset.js +24 -23
- package/calculations/core/instrument-price-change-1d.js +72 -82
- package/calculations/core/instrument-price-momentum-20d.js +66 -100
- package/calculations/core/long-position-per-stock.js +21 -13
- package/calculations/core/overall-holding-duration.js +8 -3
- package/calculations/core/overall-profitability-ratio.js +2 -2
- package/calculations/core/platform-buy-sell-sentiment.js +75 -22
- package/calculations/core/platform-daily-bought-vs-sold-count.js +19 -10
- package/calculations/core/platform-daily-ownership-delta.js +39 -15
- package/calculations/core/platform-ownership-per-sector.js +38 -18
- package/calculations/core/platform-total-positions-held.js +36 -14
- package/calculations/core/pnl-distribution-per-stock.js +39 -36
- package/calculations/core/price-metrics.js +70 -172
- package/calculations/core/profitability-ratio-per-sector.js +23 -29
- package/calculations/core/profitability-ratio-per-stock.js +20 -13
- package/calculations/core/profitability-skew-per-stock.js +20 -13
- package/calculations/core/profitable-and-unprofitable-status.js +34 -10
- package/calculations/core/sentiment-per-stock.js +20 -9
- package/calculations/core/short-position-per-stock.js +23 -37
- package/calculations/core/social-activity-aggregation.js +41 -115
- package/calculations/core/social-asset-posts-trend.js +77 -94
- package/calculations/core/social-event-correlation.js +87 -106
- package/calculations/core/social-sentiment-aggregation.js +56 -138
- package/calculations/core/social-top-mentioned-words.js +74 -106
- package/calculations/core/social-topic-interest-evolution.js +94 -94
- package/calculations/core/social-topic-sentiment-matrix.js +90 -74
- package/calculations/core/social-word-mentions-trend.js +92 -106
- package/calculations/core/speculator-asset-sentiment.js +63 -92
- package/calculations/core/speculator-danger-zone.js +77 -90
- package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +75 -90
- package/calculations/core/speculator-distance-to-tp-per-leverage.js +75 -88
- package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +75 -90
- package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +74 -89
- package/calculations/core/speculator-leverage-per-asset.js +62 -57
- package/calculations/core/speculator-leverage-per-sector.js +53 -65
- package/calculations/core/speculator-risk-reward-ratio-per-asset.js +71 -76
- package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +60 -81
- package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +57 -77
- package/calculations/core/speculator-stop-loss-per-asset.js +43 -80
- package/calculations/core/speculator-take-profit-per-asset.js +45 -69
- package/calculations/core/speculator-tsl-per-asset.js +42 -49
- package/calculations/core/total-long-figures.js +19 -19
- package/calculations/core/total-long-per-sector.js +39 -36
- package/calculations/core/total-short-figures.js +19 -19
- package/calculations/core/total-short-per-sector.js +39 -36
- package/calculations/core/users-processed.js +52 -25
- package/calculations/gauss/cohort-capital-flow.js +38 -29
- package/calculations/gauss/cohort-definer.js +17 -25
- package/calculations/gauss/daily-dna-filter.js +10 -4
- package/calculations/gauss/gauss-divergence-signal.js +28 -6
- package/calculations/gem/cohort-momentum-state.js +113 -92
- package/calculations/gem/cohort-skill-definition.js +23 -53
- package/calculations/gem/platform-conviction-divergence.js +62 -116
- package/calculations/gem/quant-skill-alpha-signal.js +107 -123
- package/calculations/gem/skilled-cohort-flow.js +178 -167
- package/calculations/gem/skilled-unskilled-divergence.js +73 -113
- package/calculations/gem/unskilled-cohort-flow.js +176 -166
- package/calculations/helix/helix-contrarian-signal.js +91 -83
- package/calculations/helix/herd-consensus-score.js +135 -97
- package/calculations/helix/winner-loser-flow.js +14 -16
- package/calculations/pyro/risk-appetite-index.js +121 -123
- package/calculations/pyro/squeeze-potential.js +93 -125
- package/calculations/pyro/volatility-signal.js +109 -97
- package/package.json +5 -5
- package/README.MD +0 -155
|
@@ -1,172 +1,90 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* --- MODIFIED ---
|
|
9
|
-
* Changed `process` method to read from `dependencies.rootData.todaySocialPostInsights`
|
|
10
|
-
* instead of a non-existent `calculationUtils.loadSocialData`.
|
|
2
|
+
* @fileoverview Calculation (Pass 2 - Meta) for social sentiment.
|
|
3
|
+
* --- FIX ---
|
|
4
|
+
* 1. Changed 'process' signature to 5-arg 'meta' standard.
|
|
5
|
+
* 2. Changed data access to read 'dateStr.social' (Arg 1).
|
|
6
|
+
* 3. Changed 'socialDoc.posts' (Object) to 'socialDoc' (Object).
|
|
7
|
+
* 4. Changed 'post.sentiment.label' to 'post.sentiment.overallSentiment'.
|
|
11
8
|
*/
|
|
12
9
|
class SocialSentimentAggregation {
|
|
10
|
+
|
|
13
11
|
constructor() {
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
this.result = {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static getMetadata() {
|
|
16
|
+
return {
|
|
17
|
+
type: 'meta',
|
|
18
|
+
rootDataDependencies: ['social'],
|
|
19
|
+
isHistorical: false,
|
|
20
|
+
userType: 'n/a',
|
|
21
|
+
category: 'core_social'
|
|
18
22
|
};
|
|
19
|
-
// { [ticker]: { bullish: 0, bearish: 0, neutral: 0 } }
|
|
20
|
-
this.perTicker = new Map();
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
static getDependencies() {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
|
|
27
29
|
static getSchema() {
|
|
28
|
-
|
|
30
|
+
return {
|
|
29
31
|
"type": "object",
|
|
32
|
+
"description": "Aggregates the overall social sentiment for the day.",
|
|
30
33
|
"properties": {
|
|
31
34
|
"bullish": { "type": "number" },
|
|
32
35
|
"bearish": { "type": "number" },
|
|
33
36
|
"neutral": { "type": "number" },
|
|
34
37
|
"total_posts": { "type": "number" },
|
|
35
|
-
"
|
|
36
|
-
"net_sentiment_pct": { "type": "number" }
|
|
37
|
-
},
|
|
38
|
-
"required": ["bullish", "bearish", "neutral", "total_posts", "net_sentiment", "net_sentiment_pct"]
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
"type": "object",
|
|
43
|
-
"description": "Aggregates social sentiment (bullish, bearish) globally and per ticker.",
|
|
44
|
-
"properties": {
|
|
45
|
-
"global_sentiment": {
|
|
46
|
-
...sentimentSchema,
|
|
47
|
-
"description": "Aggregated sentiment across all posts."
|
|
48
|
-
},
|
|
49
|
-
"per_ticker": {
|
|
50
|
-
"type": "object",
|
|
51
|
-
"description": "Aggregated sentiment broken down by asset ticker.",
|
|
52
|
-
"patternProperties": {
|
|
53
|
-
"^.*$": {
|
|
54
|
-
...sentimentSchema,
|
|
55
|
-
"description": "Aggregated sentiment for a specific ticker."
|
|
56
|
-
} // Ticker
|
|
57
|
-
},
|
|
58
|
-
"additionalProperties": sentimentSchema
|
|
59
|
-
}
|
|
38
|
+
"sentiment_score": { "type": "number", "description": "Net score (bullish-bearish) / total" }
|
|
60
39
|
},
|
|
61
|
-
"required": ["
|
|
40
|
+
"required": ["bullish", "bearish", "neutral", "total_posts", "sentiment_score"]
|
|
62
41
|
};
|
|
63
42
|
}
|
|
64
43
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
type: 'meta',
|
|
71
|
-
rootDataDependencies: ['social'],
|
|
72
|
-
isHistorical: false,
|
|
73
|
-
userType: 'n/a',
|
|
74
|
-
category: 'core_metrics'
|
|
75
|
-
};
|
|
76
|
-
}
|
|
44
|
+
// --- THIS IS THE FIX ---
|
|
45
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
46
|
+
const counts = { bullish: 0, bearish: 0, neutral: 0 };
|
|
47
|
+
let totalPosts = 0;
|
|
77
48
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*/
|
|
81
|
-
static getDependencies() {
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
49
|
+
// Get social data from Arg 1 (metaPayload)
|
|
50
|
+
const socialDoc = dateStr.social; // This IS the map of posts
|
|
84
51
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
bullish: 0,
|
|
89
|
-
bearish: 0,
|
|
90
|
-
neutral: 0
|
|
91
|
-
});
|
|
52
|
+
if (!socialDoc || typeof socialDoc !== 'object') {
|
|
53
|
+
this.result = { bullish: 0, bearish: 0, neutral: 0, total_posts: 0, sentiment_score: 0 };
|
|
54
|
+
return;
|
|
92
55
|
}
|
|
93
|
-
}
|
|
94
56
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
* @param {object} dependencies - db, logger, calculationUtils, AND rootData.
|
|
98
|
-
* @param {object} config - Computation config.
|
|
99
|
-
* @param {object} fetchedDependencies - (UNUSED) In-memory results.
|
|
100
|
-
*/
|
|
101
|
-
async process(dateStr, dependencies, config, fetchedDependencies) {
|
|
102
|
-
// --- MODIFIED ---
|
|
103
|
-
// 'meta' calcs get rootData injected into the dependencies object.
|
|
104
|
-
const todaySocialPosts = dependencies.rootData?.todaySocialPostInsights || {};
|
|
105
|
-
// --- END MODIFIED ---
|
|
57
|
+
const postsArray = Object.values(socialDoc);
|
|
58
|
+
totalPosts = postsArray.length;
|
|
106
59
|
|
|
107
|
-
for (const post of
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// 1. Aggregate global sentiment
|
|
114
|
-
if (sentiment === 'bullish') {
|
|
115
|
-
this.global.bullish++;
|
|
116
|
-
} else if (sentiment === 'bearish') {
|
|
117
|
-
this.global.bearish++;
|
|
118
|
-
} else {
|
|
119
|
-
this.global.neutral++;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// 2. Aggregate per-ticker sentiment
|
|
123
|
-
const tickers = post.tickers || [];
|
|
124
|
-
for (const ticker of tickers) {
|
|
125
|
-
this._initTicker(ticker);
|
|
126
|
-
const data = this.perTicker.get(ticker);
|
|
127
|
-
|
|
128
|
-
if (sentiment === 'bullish') {
|
|
129
|
-
data.bullish++;
|
|
130
|
-
} else if (sentiment === 'bearish') {
|
|
131
|
-
data.bearish++;
|
|
132
|
-
} else {
|
|
133
|
-
data.neutral++;
|
|
134
|
-
}
|
|
60
|
+
for (const post of postsArray) {
|
|
61
|
+
// Use 'overallSentiment' and convert to lowercase
|
|
62
|
+
const sentiment = (post.sentiment?.overallSentiment || 'neutral').toLowerCase();
|
|
63
|
+
if (counts.hasOwnProperty(sentiment)) {
|
|
64
|
+
counts[sentiment]++;
|
|
135
65
|
}
|
|
136
66
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
67
|
+
|
|
68
|
+
const score = (totalPosts > 0)
|
|
69
|
+
? ((counts.bullish - counts.bearish) / totalPosts)
|
|
70
|
+
: 0;
|
|
71
|
+
|
|
72
|
+
this.result = {
|
|
73
|
+
bullish: counts.bullish,
|
|
74
|
+
bearish: counts.bearish,
|
|
75
|
+
neutral: counts.neutral,
|
|
76
|
+
total_posts: totalPosts,
|
|
77
|
+
sentiment_score: score
|
|
148
78
|
};
|
|
149
79
|
}
|
|
80
|
+
// --- END FIX ---
|
|
150
81
|
|
|
151
|
-
async getResult() {
|
|
152
|
-
|
|
153
|
-
const global_sentiment = this._calculateSentiment(this.global);
|
|
154
|
-
|
|
155
|
-
// Calculate stats for per-ticker
|
|
156
|
-
const per_ticker = {};
|
|
157
|
-
for (const [ticker, data] of this.perTicker.entries()) {
|
|
158
|
-
per_ticker[ticker] = this._calculateSentiment(data);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
global_sentiment,
|
|
163
|
-
per_ticker
|
|
164
|
-
};
|
|
82
|
+
async getResult(fetchedDependencies) {
|
|
83
|
+
return this.result;
|
|
165
84
|
}
|
|
166
85
|
|
|
167
86
|
reset() {
|
|
168
|
-
this.
|
|
169
|
-
this.perTicker.clear();
|
|
87
|
+
this.result = {};
|
|
170
88
|
}
|
|
171
89
|
}
|
|
172
90
|
|
|
@@ -1,146 +1,114 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* trend since yesterday?"
|
|
7
|
-
*
|
|
8
|
-
* This is a stateful calculation.
|
|
2
|
+
* @fileoverview Calculation (Pass 2 - Meta) for social top words.
|
|
3
|
+
* --- FIX ---
|
|
4
|
+
* 1. Changed logic to read from 'socialDoc' directly, not 'socialDoc.posts'.
|
|
5
|
+
* 2. Changed tokenizer to read from 'post.textSnippet' to match schema.md.
|
|
9
6
|
*/
|
|
7
|
+
const STOPWORDS = new Set([
|
|
8
|
+
'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'being', 'been',
|
|
9
|
+
'it', 'its', 'it\'s', 'i', 'me', 'my', 'mine', 'you', 'your', 'yours',
|
|
10
|
+
'he', 'him', 'his', 'she', 'her', 'hers', 'they', 'them', 'their', 'theirs',
|
|
11
|
+
'we', 'us', 'our', 'ours', 'what', 'which', 'who', 'whom', 'this', 'that',
|
|
12
|
+
'these', 'those', 'am', 'and', 'but', 'if', 'or', 'because', 'as', 'until',
|
|
13
|
+
'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between',
|
|
14
|
+
'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to',
|
|
15
|
+
'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again',
|
|
16
|
+
'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how',
|
|
17
|
+
'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some',
|
|
18
|
+
'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too',
|
|
19
|
+
'very', 's', 't', 'can', 'will', 'just', 'don', 'should', 'now',
|
|
20
|
+
'stock', 'stocks', 'market', 'price', 'trade', 'trading', 'buy', 'sell',
|
|
21
|
+
'long', 'short', 'chart', 'week', 'day', 'today', 'going', 'think'
|
|
22
|
+
]);
|
|
23
|
+
|
|
10
24
|
class SocialTopMentionedWords {
|
|
25
|
+
|
|
11
26
|
constructor() {
|
|
12
|
-
|
|
13
|
-
this.wordCounts = new Map();
|
|
14
|
-
// A set of common words to ignore
|
|
15
|
-
this.stopWords = new Set(['the', 'a', 'is', 'in', 'to', 'for', 'of', 'and', 'it', 'on', 'with', 'that', 'this', 'buy', 'sell']);
|
|
27
|
+
this.result = {};
|
|
16
28
|
}
|
|
17
29
|
|
|
18
|
-
// --- NEW ---
|
|
19
|
-
/**
|
|
20
|
-
* Statically defines all metadata for the manifest builder.
|
|
21
|
-
*/
|
|
22
30
|
static getMetadata() {
|
|
23
31
|
return {
|
|
24
32
|
type: 'meta',
|
|
25
33
|
rootDataDependencies: ['social'],
|
|
26
|
-
isHistorical: false,
|
|
27
|
-
userType: 'n/a',
|
|
34
|
+
isHistorical: false,
|
|
35
|
+
userType: 'n'/'a',
|
|
28
36
|
category: 'core_social'
|
|
29
37
|
};
|
|
30
38
|
}
|
|
31
39
|
|
|
32
|
-
// --- NEW ---
|
|
33
|
-
/**
|
|
34
|
-
* Statically declare dependencies.
|
|
35
|
-
*/
|
|
36
40
|
static getDependencies() {
|
|
37
41
|
return [];
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
/**
|
|
41
|
-
* Defines the output schema for this calculation.
|
|
42
|
-
* @returns {object} JSON Schema object
|
|
43
|
-
*/
|
|
44
44
|
static getSchema() {
|
|
45
45
|
const wordSchema = {
|
|
46
46
|
"type": "object",
|
|
47
|
-
"properties": {
|
|
48
|
-
"word": { "type": "string" },
|
|
49
|
-
"count": { "type": "number" },
|
|
50
|
-
"trend_30d_pct": { "type": "number" },
|
|
51
|
-
"history_30d": { "type": "array", "items": { "type": "number" } }
|
|
52
|
-
},
|
|
53
|
-
"required": ["word", "count", "trend_30d_pct", "history_30d"]
|
|
47
|
+
"properties": { "count": { "type": "number" } }
|
|
54
48
|
};
|
|
55
49
|
|
|
56
50
|
return {
|
|
57
|
-
"type": "
|
|
58
|
-
"description": "
|
|
59
|
-
"
|
|
51
|
+
"type": "object",
|
|
52
|
+
"description": "Calculates the top 50 most mentioned words in social posts.",
|
|
53
|
+
"patternProperties": { "^.*$": wordSchema },
|
|
54
|
+
"additionalProperties": wordSchema
|
|
60
55
|
};
|
|
61
56
|
}
|
|
62
57
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
58
|
+
_tokenize(text) {
|
|
59
|
+
if (!text) return [];
|
|
60
|
+
return text.toLowerCase()
|
|
61
|
+
.replace(/[^\w\s$]/g, '')
|
|
62
|
+
.split(/\s+/)
|
|
63
|
+
.filter(word =>
|
|
64
|
+
word.length > 2 &&
|
|
65
|
+
!STOPWORDS.has(word) &&
|
|
66
|
+
!/^\d+$/.test(word)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
71
|
+
const wordCounts = new Map();
|
|
72
|
+
|
|
73
|
+
// 'dateStr' (Arg 1) is the metaPayload from the worker
|
|
74
|
+
const socialDoc = dateStr.social; // This IS the map of posts
|
|
75
|
+
|
|
76
|
+
// --- THIS IS THE FIX (Part 1) ---
|
|
77
|
+
// 'socialDoc' is the map of posts, not an object containing .posts
|
|
78
|
+
if (!socialDoc || typeof socialDoc !== 'object') {
|
|
79
|
+
// --- END FIX (Part 1) ---
|
|
80
|
+
this.result = {};
|
|
81
|
+
return;
|
|
83
82
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
const text = (post.fullText || '').toLowerCase().replace(/[^a-z\s]/g, '');
|
|
91
|
-
const words = text.split(/\s+/);
|
|
92
|
-
|
|
83
|
+
|
|
84
|
+
for (const post of Object.values(socialDoc)) {
|
|
85
|
+
// --- THIS IS THE FIX (Part 2) ---
|
|
86
|
+
// schema.md provides 'textSnippet', not 'fullText'
|
|
87
|
+
const words = this._tokenize(post.textSnippet);
|
|
88
|
+
// --- END FIX (Part 2) ---
|
|
93
89
|
for (const word of words) {
|
|
94
|
-
|
|
95
|
-
if (!this.wordCounts.has(word)) {
|
|
96
|
-
this.wordCounts.set(word, { count: 0, history: [] });
|
|
97
|
-
}
|
|
98
|
-
this.wordCounts.get(word).count++;
|
|
99
|
-
}
|
|
90
|
+
wordCounts.set(word, (wordCounts.get(word) || 0) + 1);
|
|
100
91
|
}
|
|
101
92
|
}
|
|
102
|
-
}
|
|
103
93
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
for (const [word, data] of this.wordCounts.entries()) {
|
|
108
|
-
const history = data.history;
|
|
109
|
-
const todayCount = data.count;
|
|
110
|
-
|
|
111
|
-
// Don't include words that weren't mentioned today
|
|
112
|
-
if (todayCount === 0) continue;
|
|
113
|
-
|
|
114
|
-
const newHistory = [todayCount, ...history].slice(0, 30);
|
|
115
|
-
|
|
116
|
-
let trend_30d_pct = 0;
|
|
117
|
-
if (history.length > 0) {
|
|
118
|
-
const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
|
|
119
|
-
if (avg_30d > 0) {
|
|
120
|
-
trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
94
|
+
const sortedWords = Array.from(wordCounts.entries())
|
|
95
|
+
.sort((a, b) => b[1] - a[1])
|
|
96
|
+
.slice(0, 50);
|
|
123
97
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
trend_30d_pct: trend_30d_pct,
|
|
128
|
-
history_30d: newHistory
|
|
129
|
-
});
|
|
98
|
+
const result = {};
|
|
99
|
+
for (const [word, count] of sortedWords) {
|
|
100
|
+
result[word] = { count: count };
|
|
130
101
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// Return only the top 50 for the API.
|
|
139
|
-
return allWords.slice(0, 50);
|
|
102
|
+
|
|
103
|
+
this.result = result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async getResult(fetchedDependencies) {
|
|
107
|
+
return this.result;
|
|
140
108
|
}
|
|
141
109
|
|
|
142
110
|
reset() {
|
|
143
|
-
this.
|
|
111
|
+
this.result = {};
|
|
144
112
|
}
|
|
145
113
|
}
|
|
146
114
|
|