aiden-shared-calculations-unified 1.0.82 → 1.0.84

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.
Files changed (71) hide show
  1. package/calculations/core/asset-pnl-status.js +122 -104
  2. package/calculations/core/asset-position-size.js +110 -73
  3. package/calculations/core/average-daily-pnl-all-users.js +17 -3
  4. package/calculations/core/average-daily-pnl-per-sector.js +83 -75
  5. package/calculations/core/average-daily-pnl-per-stock.js +84 -73
  6. package/calculations/core/average-daily-position-pnl.js +2 -2
  7. package/calculations/core/holding-duration-per-asset.js +24 -23
  8. package/calculations/core/instrument-price-change-1d.js +72 -82
  9. package/calculations/core/instrument-price-momentum-20d.js +66 -100
  10. package/calculations/core/long-position-per-stock.js +21 -13
  11. package/calculations/core/overall-holding-duration.js +8 -3
  12. package/calculations/core/overall-profitability-ratio.js +2 -2
  13. package/calculations/core/platform-buy-sell-sentiment.js +75 -22
  14. package/calculations/core/platform-daily-bought-vs-sold-count.js +19 -10
  15. package/calculations/core/platform-daily-ownership-delta.js +39 -15
  16. package/calculations/core/platform-ownership-per-sector.js +38 -18
  17. package/calculations/core/platform-total-positions-held.js +36 -14
  18. package/calculations/core/pnl-distribution-per-stock.js +39 -36
  19. package/calculations/core/price-metrics.js +70 -172
  20. package/calculations/core/profitability-ratio-per-sector.js +23 -29
  21. package/calculations/core/profitability-ratio-per-stock.js +20 -13
  22. package/calculations/core/profitability-skew-per-stock.js +20 -13
  23. package/calculations/core/profitable-and-unprofitable-status.js +34 -10
  24. package/calculations/core/sentiment-per-stock.js +20 -9
  25. package/calculations/core/short-position-per-stock.js +23 -37
  26. package/calculations/core/social-activity-aggregation.js +41 -115
  27. package/calculations/core/social-asset-posts-trend.js +77 -94
  28. package/calculations/core/social-event-correlation.js +87 -106
  29. package/calculations/core/social-sentiment-aggregation.js +56 -138
  30. package/calculations/core/social-top-mentioned-words.js +74 -106
  31. package/calculations/core/social-topic-interest-evolution.js +94 -94
  32. package/calculations/core/social-topic-sentiment-matrix.js +90 -74
  33. package/calculations/core/social-word-mentions-trend.js +92 -106
  34. package/calculations/core/speculator-asset-sentiment.js +63 -92
  35. package/calculations/core/speculator-danger-zone.js +77 -90
  36. package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +75 -90
  37. package/calculations/core/speculator-distance-to-tp-per-leverage.js +75 -88
  38. package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +75 -90
  39. package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +74 -89
  40. package/calculations/core/speculator-leverage-per-asset.js +62 -57
  41. package/calculations/core/speculator-leverage-per-sector.js +53 -65
  42. package/calculations/core/speculator-risk-reward-ratio-per-asset.js +71 -76
  43. package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +60 -81
  44. package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +57 -77
  45. package/calculations/core/speculator-stop-loss-per-asset.js +43 -80
  46. package/calculations/core/speculator-take-profit-per-asset.js +45 -69
  47. package/calculations/core/speculator-tsl-per-asset.js +42 -49
  48. package/calculations/core/total-long-figures.js +19 -19
  49. package/calculations/core/total-long-per-sector.js +39 -36
  50. package/calculations/core/total-short-figures.js +19 -19
  51. package/calculations/core/total-short-per-sector.js +39 -36
  52. package/calculations/core/users-processed.js +52 -25
  53. package/calculations/gauss/cohort-capital-flow.js +38 -29
  54. package/calculations/gauss/cohort-definer.js +17 -25
  55. package/calculations/gauss/daily-dna-filter.js +10 -4
  56. package/calculations/gauss/gauss-divergence-signal.js +28 -6
  57. package/calculations/gem/cohort-momentum-state.js +113 -92
  58. package/calculations/gem/cohort-skill-definition.js +23 -53
  59. package/calculations/gem/platform-conviction-divergence.js +62 -116
  60. package/calculations/gem/quant-skill-alpha-signal.js +107 -123
  61. package/calculations/gem/skilled-cohort-flow.js +178 -167
  62. package/calculations/gem/skilled-unskilled-divergence.js +73 -113
  63. package/calculations/gem/unskilled-cohort-flow.js +176 -166
  64. package/calculations/helix/helix-contrarian-signal.js +91 -83
  65. package/calculations/helix/herd-consensus-score.js +135 -97
  66. package/calculations/helix/winner-loser-flow.js +14 -14
  67. package/calculations/pyro/risk-appetite-index.js +121 -123
  68. package/calculations/pyro/squeeze-potential.js +93 -125
  69. package/calculations/pyro/volatility-signal.js +109 -97
  70. package/package.json +9 -9
  71. package/README.MD +0 -78
@@ -1,172 +1,90 @@
1
1
  /**
2
- * @fileoverview Social Calculation (Pass 1)
3
- *
4
- * This metric answers: "What is the aggregated social media
5
- * sentiment (bullish, bearish, neutral) globally and
6
- * for each ticker?"
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.global = {
15
- bullish: 0,
16
- bearish: 0,
17
- neutral: 0
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
- * Defines the output schema for this calculation.
25
- * @returns {object} JSON Schema object
26
- */
25
+ static getDependencies() {
26
+ return [];
27
+ }
28
+
27
29
  static getSchema() {
28
- const sentimentSchema = {
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
- "net_sentiment": { "type": "number" },
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": ["global_sentiment", "per_ticker"]
40
+ "required": ["bullish", "bearish", "neutral", "total_posts", "sentiment_score"]
62
41
  };
63
42
  }
64
43
 
65
- /**
66
- * Statically defines all metadata for the manifest builder.
67
- */
68
- static getMetadata() {
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
- * Statically declare dependencies.
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
- _initTicker(ticker) {
86
- if (!this.perTicker.has(ticker)) {
87
- this.perTicker.set(ticker, {
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
- * @param {string} dateStr - Today's date.
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 Object.values(todaySocialPosts)) {
108
- // This logic assumes 'sentiment' is a simple string,
109
- // not the object from gemini. This is correct for this calc.
110
- // --- MODIFIED: Read from structured sentiment object ---
111
- const sentiment = post.sentiment?.overallSentiment?.toLowerCase() || 'neutral';
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
- _calculateSentiment(data) {
140
- const total = data.bullish + data.bearish + data.neutral;
141
- const net = data.bullish - data.bearish;
142
- return {
143
- ...data,
144
- total_posts: total,
145
- net_sentiment: net,
146
- // Net sentiment as a percentage of *all* posts (bullish, bearish, neutral)
147
- net_sentiment_pct: (total > 0) ? (net / total) * 100 : 0
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
- // Calculate stats for global
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.global = { bullish: 0, bearish: 0, neutral: 0 };
169
- this.perTicker.clear();
87
+ this.result = {};
170
88
  }
171
89
  }
172
90
 
@@ -1,146 +1,114 @@
1
1
  /**
2
- * @fileoverview Social Calculation (Pass 1)
3
- *
4
- * This metric answers: "What are the top 50 most mentioned
5
- * words in social media posts today, and what is their
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
- // { [word]: { count: 0, history: [] } }
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, // Stateful
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": "array",
58
- "description": "Top 50 most mentioned words (non-stop words) with their 30-day trend.",
59
- "items": wordSchema
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
- // --- REFACTORED ---
64
- /**
65
- * @param {string} dateStr - Today's date.
66
- * @param {object} rootData - The root data object.
67
- * @param {object} dependencies - db, logger, calculationUtils
68
- */
69
- async process(dateStr, rootData, dependencies) {
70
- const { calculationUtils } = dependencies;
71
-
72
- // 1. Load this metric's history from yesterday
73
- // Note: History is stored by word
74
- const yHistoryData = await calculationUtils.loadYesterdayData('social-top-mentioned-words-history'); // Fictional helper
75
-
76
- if (yHistoryData) {
77
- for (const [word, data] of Object.entries(yHistoryData)) {
78
- this.wordCounts.set(word, {
79
- count: 0,
80
- history: data.history_30d || []
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
- // 2. Process today's raw social data
86
- const todaySocialPosts = rootData.todaySocialPostInsights || {};
87
-
88
- for (const post of Object.values(todaySocialPosts)) {
89
- // Use `fullText` for complete analysis
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
- if (word.length > 2 && !this.stopWords.has(word)) {
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
- async getResult() {
105
- const allWords = [];
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
- allWords.push({
125
- word: word,
126
- count: todayCount,
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
- // Sort by today's count and take top 50
133
- allWords.sort((a, b) => b.count - a.count);
134
-
135
- // The *full* result (for history) should be saved to a different
136
- // doc ('social-top-mentioned-words-history') by the computation system.
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.wordCounts.clear();
111
+ this.result = {};
144
112
  }
145
113
  }
146
114