aiden-shared-calculations-unified 1.0.84 → 1.0.87

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 (70) hide show
  1. package/calculations/core/asset-pnl-status.js +36 -106
  2. package/calculations/core/asset-position-size.js +40 -91
  3. package/calculations/core/average-daily-pnl-all-users.js +18 -57
  4. package/calculations/core/average-daily-pnl-per-sector.js +41 -88
  5. package/calculations/core/average-daily-pnl-per-stock.js +38 -91
  6. package/calculations/core/average-daily-position-pnl.js +19 -49
  7. package/calculations/core/holding-duration-per-asset.js +25 -127
  8. package/calculations/core/instrument-price-change-1d.js +30 -49
  9. package/calculations/core/instrument-price-momentum-20d.js +50 -60
  10. package/calculations/core/long-position-per-stock.js +39 -68
  11. package/calculations/core/overall-holding-duration.js +16 -87
  12. package/calculations/core/overall-profitability-ratio.js +11 -40
  13. package/calculations/core/platform-buy-sell-sentiment.js +41 -124
  14. package/calculations/core/platform-daily-bought-vs-sold-count.js +41 -99
  15. package/calculations/core/platform-daily-ownership-delta.js +68 -126
  16. package/calculations/core/platform-ownership-per-sector.js +45 -96
  17. package/calculations/core/platform-total-positions-held.js +20 -80
  18. package/calculations/core/pnl-distribution-per-stock.js +29 -135
  19. package/calculations/core/price-metrics.js +95 -206
  20. package/calculations/core/profitability-ratio-per-sector.js +34 -79
  21. package/calculations/core/profitability-ratio-per-stock.js +32 -88
  22. package/calculations/core/profitability-skew-per-stock.js +41 -94
  23. package/calculations/core/profitable-and-unprofitable-status.js +44 -76
  24. package/calculations/core/sentiment-per-stock.js +24 -77
  25. package/calculations/core/short-position-per-stock.js +35 -43
  26. package/calculations/core/social-activity-aggregation.js +26 -49
  27. package/calculations/core/social-asset-posts-trend.js +38 -94
  28. package/calculations/core/social-event-correlation.js +26 -93
  29. package/calculations/core/social-sentiment-aggregation.js +20 -44
  30. package/calculations/core/social-top-mentioned-words.js +35 -87
  31. package/calculations/core/social-topic-interest-evolution.js +22 -111
  32. package/calculations/core/social-topic-sentiment-matrix.js +38 -104
  33. package/calculations/core/social-word-mentions-trend.js +27 -104
  34. package/calculations/core/speculator-asset-sentiment.js +31 -72
  35. package/calculations/core/speculator-danger-zone.js +48 -84
  36. package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +20 -52
  37. package/calculations/core/speculator-distance-to-tp-per-leverage.js +23 -53
  38. package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +20 -50
  39. package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +23 -50
  40. package/calculations/core/speculator-leverage-per-asset.js +25 -64
  41. package/calculations/core/speculator-leverage-per-sector.js +27 -63
  42. package/calculations/core/speculator-risk-reward-ratio-per-asset.js +24 -53
  43. package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +55 -68
  44. package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +54 -71
  45. package/calculations/core/speculator-stop-loss-per-asset.js +19 -44
  46. package/calculations/core/speculator-take-profit-per-asset.js +20 -57
  47. package/calculations/core/speculator-tsl-per-asset.js +17 -56
  48. package/calculations/core/total-long-figures.js +16 -31
  49. package/calculations/core/total-long-per-sector.js +39 -61
  50. package/calculations/core/total-short-figures.js +13 -32
  51. package/calculations/core/total-short-per-sector.js +39 -61
  52. package/calculations/core/users-processed.js +11 -46
  53. package/calculations/gauss/cohort-capital-flow.js +54 -173
  54. package/calculations/gauss/cohort-definer.js +77 -163
  55. package/calculations/gauss/daily-dna-filter.js +29 -83
  56. package/calculations/gauss/gauss-divergence-signal.js +22 -109
  57. package/calculations/gem/cohort-momentum-state.js +27 -72
  58. package/calculations/gem/cohort-skill-definition.js +36 -52
  59. package/calculations/gem/platform-conviction-divergence.js +18 -60
  60. package/calculations/gem/quant-skill-alpha-signal.js +25 -98
  61. package/calculations/gem/skilled-cohort-flow.js +67 -175
  62. package/calculations/gem/skilled-unskilled-divergence.js +18 -73
  63. package/calculations/gem/unskilled-cohort-flow.js +64 -172
  64. package/calculations/helix/helix-contrarian-signal.js +20 -114
  65. package/calculations/helix/herd-consensus-score.js +42 -124
  66. package/calculations/helix/winner-loser-flow.js +36 -118
  67. package/calculations/pyro/risk-appetite-index.js +33 -74
  68. package/calculations/pyro/squeeze-potential.js +30 -87
  69. package/calculations/pyro/volatility-signal.js +33 -78
  70. package/package.json +1 -1
@@ -1,13 +1,10 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for short positions per stock.
3
- * --- FIX ---
4
- * - Logic updated to check for *both* Speculator schema (pos.IsBuy === false)
5
- * and Normal schema (pos.Direction === 'Sell') to match schema.md.
3
+ * REFACTORED: Tracks exposure weight (percentage).
6
4
  */
7
-
8
5
  class ShortPositionPerStock {
9
6
  constructor() {
10
- this.assets = new Map();
7
+ this.stockData = new Map();
11
8
  this.tickerMap = null;
12
9
  }
13
10
 
@@ -21,70 +18,65 @@ class ShortPositionPerStock {
21
18
  };
22
19
  }
23
20
 
24
- static getDependencies() {
25
- return [];
26
- }
21
+ static getDependencies() { return []; }
27
22
 
28
23
  static getSchema() {
29
- return {
24
+ const schema = {
30
25
  "type": "object",
31
- "description": "Calculates the total count of short ('sell') positions for each asset.",
32
- "patternProperties": {
33
- "^.*$": { "type": "number" }
26
+ "properties": {
27
+ "short_count": { "type": "number" },
28
+ "total_short_exposure_weight": { "type": "number", "description": "Total allocation % for short positions." }
34
29
  },
35
- "additionalProperties": { "type": "number" }
30
+ "required": ["short_count", "total_short_exposure_weight"]
36
31
  };
32
+ return { "type": "object", "patternProperties": { "^.*$": schema } };
37
33
  }
38
34
 
39
- _initAsset(instrumentId) {
40
- if (!this.assets.has(instrumentId)) {
41
- this.assets.set(instrumentId, 0);
35
+ _initStock(instId) {
36
+ if (!this.stockData.has(instId)) {
37
+ this.stockData.set(instId, { count: 0, weight: 0 });
42
38
  }
43
39
  }
44
40
 
45
- process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
46
- if (!this.tickerMap) {
47
- this.tickerMap = context.instrumentToTicker;
48
- }
41
+ process(context) {
42
+ const { extract } = context.math;
43
+ const { mappings, user } = context;
44
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
49
45
 
50
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
51
- if (!positions || !Array.isArray(positions)) {
52
- return;
53
- }
46
+ const positions = extract.getPositions(user.portfolio.today, user.type);
54
47
 
55
48
  for (const pos of positions) {
56
- // --- THIS IS THE FIX ---
57
- // Check for Speculator (IsBuy) OR Normal (Direction)
58
- const isShort = (pos.IsBuy === false) || (pos.Direction === 'Sell');
49
+ const direction = extract.getDirection(pos);
50
+ if (direction !== 'Sell') continue; // Only Shorts
59
51
 
60
- if (isShort) {
61
- // --- END FIX ---
62
- const instrumentId = pos.InstrumentID;
63
- if (!instrumentId) continue;
52
+ const instId = extract.getInstrumentId(pos);
53
+ if (!instId) continue;
64
54
 
65
- this._initAsset(instrumentId);
66
- this.assets.set(instrumentId, this.assets.get(instrumentId) + 1);
67
- }
55
+ const weight = extract.getPositionWeight(pos, user.type);
56
+
57
+ this._initStock(instId);
58
+ const data = this.stockData.get(instId);
59
+ data.count++;
60
+ data.weight += weight;
68
61
  }
69
62
  }
70
63
 
71
64
  async getResult() {
72
- if (!this.tickerMap) {
73
- return {};
74
- }
75
-
65
+ if (!this.tickerMap) return {};
76
66
  const result = {};
77
- for (const [instrumentId, count] of this.assets.entries()) {
78
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
79
- result[ticker] = count;
67
+ for (const [instId, data] of this.stockData.entries()) {
68
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
69
+ result[ticker] = {
70
+ short_count: data.count,
71
+ total_short_exposure_weight: data.weight
72
+ };
80
73
  }
81
74
  return result;
82
75
  }
83
76
 
84
77
  reset() {
85
- this.assets.clear();
78
+ this.stockData.clear();
86
79
  this.tickerMap = null;
87
80
  }
88
81
  }
89
-
90
82
  module.exports = ShortPositionPerStock;
@@ -1,17 +1,9 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 2 - Meta) for social activity.
3
- * --- FIX ---
4
- * 1. Changed 'process' signature to 5-arg 'meta' standard.
5
- * 2. Changed data access to read 'dateStr.social' (Arg 1, the metaPayload).
6
- * 3. Changed 'socialDoc.posts' to 'socialDoc' (which IS the map of posts).
7
- * 4. Changed 'post.comments' to 'post.commentCount'.
8
- * 5. Changed 'post.likes' to 'post.likeCount'.
3
+ * REFACTORED: Uses context.social.today.
9
4
  */
10
5
  class SocialActivityAggregation {
11
-
12
- constructor() {
13
- this.result = {};
14
- }
6
+ constructor() { this.result = {}; }
15
7
 
16
8
  static getMetadata() {
17
9
  return {
@@ -23,60 +15,45 @@ class SocialActivityAggregation {
23
15
  };
24
16
  }
25
17
 
26
- static getDependencies() {
27
- return [];
28
- }
18
+ static getDependencies() { return []; }
29
19
 
30
20
  static getSchema() {
31
21
  return {
32
22
  "type": "object",
33
- "description": "Aggregates the total social activity for the day.",
34
23
  "properties": {
35
24
  "total_posts": { "type": "number" },
36
- "total_comments": { "type": "number" },
37
- "total_likes": { "type": "number" }
25
+ "unique_authors": { "type": "number" },
26
+ "total_likes": { "type": "number" },
27
+ "total_comments": { "type": "number" }
38
28
  },
39
- "required": ["total_posts", "total_comments", "total_likes"]
29
+ "required": ["total_posts", "unique_authors", "total_likes", "total_comments"]
40
30
  };
41
31
  }
42
32
 
43
- // --- THIS IS THE FIX ---
44
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
45
-
46
- // Get social data from Arg 1 (metaPayload)
47
- const socialDoc = dateStr.social; // This IS the map of posts
48
-
49
- if (!socialDoc || typeof socialDoc !== 'object') {
50
- this.result = { total_posts: 0, total_comments: 0, total_likes: 0 };
51
- return;
52
- }
53
-
54
- const postsArray = Object.values(socialDoc);
55
- let totalPosts = postsArray.length;
56
- let totalComments = 0;
57
- let totalLikes = 0;
58
-
59
- for (const post of postsArray) {
60
- // Use schema.md fields
61
- totalComments += post.commentCount || 0;
62
- totalLikes += post.likeCount || 0;
33
+ process(context) {
34
+ // Access social data from context
35
+ const socialPosts = context.social?.today || {};
36
+
37
+ let posts = 0, likes = 0, comments = 0;
38
+ const authors = new Set();
39
+
40
+ for (const postId in socialPosts) {
41
+ const post = socialPosts[postId];
42
+ posts++;
43
+ likes += (post.likeCount || 0);
44
+ comments += (post.commentCount || 0);
45
+ if (post.postOwnerId) authors.add(post.postOwnerId);
63
46
  }
64
47
 
65
48
  this.result = {
66
- total_posts: totalPosts,
67
- total_comments: totalComments,
68
- total_likes: totalLikes
49
+ total_posts: posts,
50
+ unique_authors: authors.size,
51
+ total_likes: likes,
52
+ total_comments: comments
69
53
  };
70
54
  }
71
- // --- END FIX ---
72
-
73
- async getResult(fetchedDependencies) {
74
- return this.result;
75
- }
76
55
 
77
- reset() {
78
- this.result = {};
79
- }
56
+ async getResult() { return this.result; }
57
+ reset() { this.result = {}; }
80
58
  }
81
-
82
59
  module.exports = SocialActivityAggregation;
@@ -1,125 +1,69 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 2 - Meta) for social posts trend.
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.timestamp' to 'post.createdAt'.
8
- * 5. Changed 'post.instrumentIds' to 'post.tickers'.
9
- * 6. Removed 'instrumentToTicker' mapping as 'post.tickers' is already tickers.
2
+ * @fileoverview Calculation (Pass 2 - Meta) for asset mention trends.
3
+ * REFACTORED: Aggregates post counts per ticker.
10
4
  */
11
-
12
5
  class SocialAssetPostsTrend {
13
-
14
- constructor() {
15
- this.result = {};
16
- }
6
+ constructor() { this.result = {}; }
17
7
 
18
8
  static getMetadata() {
19
9
  return {
20
10
  type: 'meta',
21
11
  rootDataDependencies: ['social'],
22
- isHistorical: true,
12
+ isHistorical: false,
23
13
  userType: 'n/a',
24
14
  category: 'core_social'
25
15
  };
26
16
  }
27
17
 
28
- static getDependencies() {
29
- return [];
30
- }
18
+ static getDependencies() { return []; }
31
19
 
32
20
  static getSchema() {
33
21
  const tickerSchema = {
34
22
  "type": "object",
35
23
  "properties": {
36
- "posts_1d_pct": { "type": "number" },
37
- "posts_7d_pct": { "type": "number" },
38
- "trend_ratio": { "type": ["number", "null"] }
24
+ "post_count": { "type": "number" },
25
+ "sentiment_score": { "type": "number" }
39
26
  },
40
- "required": ["posts_1d_pct", "posts_7d_pct", "trend_ratio"]
41
- };
42
-
43
- return {
44
- "type": "object",
45
- "description": "Calculates the 1-day vs 7-day percentage of total posts for each asset.",
46
- "patternProperties": { "^.*$": tickerSchema },
47
- "additionalProperties": tickerSchema
27
+ "required": ["post_count", "sentiment_score"]
48
28
  };
29
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
49
30
  }
50
31
 
51
- // --- THIS IS THE FIX ---
52
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
53
-
54
- // Get social data from Arg 1 (metaPayload)
55
- const socialDoc = dateStr.social; // This IS the map of posts
56
- // Get the date from Arg 1
57
- const todayDateStr = dateStr.date;
58
-
59
- if (!socialDoc || typeof socialDoc !== 'object' || !todayDateStr) {
60
- this.result = {};
61
- return;
62
- }
63
-
64
- const today = new Date(todayDateStr + 'T00:00:00Z');
65
- const sevenDaysAgo = new Date(today);
66
- sevenDaysAgo.setUTCDate(today.getUTCDate() - 7);
67
-
68
- const counts1d = new Map();
69
- const counts7d = new Map();
70
- let total1d = 0;
71
- let total7d = 0;
72
-
73
- for (const post of Object.values(socialDoc)) {
74
- const postDate = new Date(post.createdAt); // Use 'createdAt'
75
- const is1d = postDate.toISOString().slice(0, 10) === todayDateStr;
76
- const is7d = postDate >= sevenDaysAgo;
77
-
78
- if (!is7d) continue;
79
-
80
- // Use 'tickers' array directly from schema
81
- for (const ticker of (post.tickers || [])) {
82
- if (!ticker) continue;
83
-
84
- if (is7d) {
85
- counts7d.set(ticker, (counts7d.get(ticker) || 0) + 1);
86
- total7d++;
87
- }
88
- if (is1d) {
89
- counts1d.set(ticker, (counts1d.get(ticker) || 0) + 1);
90
- total1d++;
91
- }
32
+ process(context) {
33
+ const socialPosts = context.social?.today || {};
34
+ const assetStats = new Map(); // { ticker: { count: 0, sentimentSum: 0 } }
35
+
36
+ for (const postId in socialPosts) {
37
+ const post = socialPosts[postId];
38
+ if (!post.tickers || !Array.isArray(post.tickers)) continue;
39
+
40
+ // Convert sentiment to numeric: Bullish=1, Bearish=-1, Neutral=0
41
+ let sentVal = 0;
42
+ const sentiment = (post.sentiment?.overallSentiment || '').toLowerCase();
43
+ if (sentiment === 'bullish') sentVal = 1;
44
+ else if (sentiment === 'bearish') sentVal = -1;
45
+
46
+ for (const ticker of post.tickers) {
47
+ const t = ticker.toUpperCase();
48
+ if (!assetStats.has(t)) assetStats.set(t, { count: 0, sentimentSum: 0 });
49
+
50
+ const stats = assetStats.get(t);
51
+ stats.count++;
52
+ stats.sentimentSum += sentVal;
92
53
  }
93
54
  }
94
55
 
95
- const result = {};
96
- const allTickers = new Set([...counts1d.keys(), ...counts7d.keys()]);
97
-
98
- for (const ticker of allTickers) {
99
- const c1 = counts1d.get(ticker) || 0;
100
- const c7 = counts7d.get(ticker) || 0;
101
-
102
- const pct1d = (total1d > 0) ? (c1 / total1d) * 100 : 0;
103
- const pct7d = (total7d > 0) ? (c7 / total7d) * 100 : 0;
104
-
105
- result[ticker] = {
106
- posts_1d_pct: pct1d,
107
- posts_7d_pct: pct7d,
108
- trend_ratio: (pct7d > 0) ? (pct1d / pct7d) : null
56
+ const output = {};
57
+ for (const [ticker, stats] of assetStats.entries()) {
58
+ output[ticker] = {
59
+ post_count: stats.count,
60
+ sentiment_score: stats.count > 0 ? stats.sentimentSum / stats.count : 0
109
61
  };
110
62
  }
111
-
112
- this.result = result;
113
- }
114
- // --- END FIX ---
115
-
116
- async getResult(fetchedDependencies) {
117
- return this.result;
63
+ this.result = output;
118
64
  }
119
65
 
120
- reset() {
121
- this.result = {};
122
- }
66
+ async getResult() { return this.result; }
67
+ reset() { this.result = {}; }
123
68
  }
124
-
125
69
  module.exports = SocialAssetPostsTrend;
@@ -1,17 +1,9 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 2 - Meta) for social event correlation.
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' to 'socialDoc' (Object).
7
- * 4. **Rewrote logic** to infer events from 'post.pollData' and
8
- * 'post.sentiment.topics' as 'post.event' does not exist in schema.
2
+ * @fileoverview Calculation (Pass 2 - Meta) for social event extraction.
3
+ * REFACTORED: Analyzes events from social posts.
9
4
  */
10
5
  class SocialEventCorrelation {
11
-
12
- constructor() {
13
- this.result = {};
14
- }
6
+ constructor() { this.result = {}; }
15
7
 
16
8
  static getMetadata() {
17
9
  return {
@@ -23,103 +15,44 @@ class SocialEventCorrelation {
23
15
  };
24
16
  }
25
17
 
26
- static getDependencies() {
27
- return [];
28
- }
18
+ static getDependencies() { return []; }
29
19
 
30
20
  static getSchema() {
31
- const eventSchema = {
32
- "type": "object",
33
- "properties": {
34
- "topic_distribution": { "type": "object" },
35
- "sentiment_distribution": { "type": "object" },
36
- "post_count": { "type": "number" }
37
- }
38
- };
39
-
40
21
  return {
41
22
  "type": "object",
42
- "description": "Correlates social events (e.g., 'poll') with topic and sentiment distributions.",
43
- "properties": {
44
- "poll": eventSchema,
45
- "earnings": eventSchema, // Inferred from topics
46
- "launch": eventSchema // Inferred from topics
47
- }
23
+ "description": "Map of events detected in social stream.",
24
+ "patternProperties": { "^.*$": { "type": "number", "description": "Count of mentions" } }
48
25
  };
49
26
  }
50
27
 
51
- _initEvent(result, eventName) {
52
- if (!result[eventName]) {
53
- result[eventName] = {
54
- topic_distribution: {},
55
- sentiment_distribution: {},
56
- post_count: 0
57
- };
58
- }
59
- }
60
-
61
- // --- THIS IS THE FIX ---
62
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
63
- const result = {};
64
-
65
- // Get social data from Arg 1 (metaPayload)
66
- const socialDoc = dateStr.social; // This IS the map of posts
67
-
68
- if (!socialDoc || typeof socialDoc !== 'object') {
69
- this.result = {};
70
- return;
71
- }
72
-
73
- for (const post of Object.values(socialDoc)) {
28
+ process(context) {
29
+ const socialPosts = context.social?.today || {};
30
+ const events = new Map();
74
31
 
75
- // Infer events from schema.md fields
32
+ for (const postId in socialPosts) {
33
+ const post = socialPosts[postId];
34
+ // Extract 'topics' from the sentiment analysis result
76
35
  const topics = post.sentiment?.topics || [];
77
- const sentiment = post.sentiment?.overallSentiment || 'Neutral';
78
-
79
- const detectedEvents = new Set();
80
-
81
- // Event 1: Check for Polls
82
- if (post.pollData) {
83
- detectedEvents.add('poll');
84
- }
85
-
86
- // Event 2: Check for Topics as Events
87
- if (topics.includes('Earnings')) {
88
- detectedEvents.add('earnings');
89
- }
90
- // Add any other topic-based events you want to track
91
- // if (topics.includes('Launch')) {
92
- // detectedEvents.add('launch');
93
- // }
94
-
95
- if (detectedEvents.size === 0) continue;
96
-
97
- for (const eventName of detectedEvents) {
98
- this._initEvent(result, eventName);
99
- const eventData = result[eventName];
100
- eventData.post_count++;
101
-
102
- // Aggregate topics
36
+
37
+ if (Array.isArray(topics)) {
103
38
  for (const topic of topics) {
104
- eventData.topic_distribution[topic] = (eventData.topic_distribution[topic] || 0) + 1;
39
+ // Basic cleanup
40
+ const cleanTopic = topic.trim().toUpperCase();
41
+ if (cleanTopic.length > 2) {
42
+ events.set(cleanTopic, (events.get(cleanTopic) || 0) + 1);
43
+ }
105
44
  }
106
-
107
- // Aggregate sentiment
108
- eventData.sentiment_distribution[sentiment] = (eventData.sentiment_distribution[sentiment] || 0) + 1;
109
45
  }
110
46
  }
47
+ // Filter for significant events (mentioned > 1 times)
48
+ const significant = [...events.entries()]
49
+ .filter(([_, count]) => count > 1)
50
+ .sort((a, b) => b[1] - a[1]);
111
51
 
112
- this.result = result;
52
+ this.result = Object.fromEntries(significant);
113
53
  }
114
- // --- END FIX ---
115
54
 
116
- async getResult(fetchedDependencies) {
117
- return this.result;
118
- }
119
-
120
- reset() {
121
- this.result = {};
122
- }
55
+ async getResult() { return this.result; }
56
+ reset() { this.result = {}; }
123
57
  }
124
-
125
58
  module.exports = SocialEventCorrelation;
@@ -1,16 +1,9 @@
1
1
  /**
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'.
2
+ * @fileoverview Calculation (Pass 2 - Meta) for sentiment aggregation.
3
+ * REFACTORED: Uses context.social.today and sentiment analysis results.
8
4
  */
9
5
  class SocialSentimentAggregation {
10
-
11
- constructor() {
12
- this.result = {};
13
- }
6
+ constructor() { this.result = {}; }
14
7
 
15
8
  static getMetadata() {
16
9
  return {
@@ -22,70 +15,53 @@ class SocialSentimentAggregation {
22
15
  };
23
16
  }
24
17
 
25
- static getDependencies() {
26
- return [];
27
- }
18
+ static getDependencies() { return []; }
28
19
 
29
20
  static getSchema() {
30
21
  return {
31
22
  "type": "object",
32
- "description": "Aggregates the overall social sentiment for the day.",
33
23
  "properties": {
34
24
  "bullish": { "type": "number" },
35
25
  "bearish": { "type": "number" },
36
26
  "neutral": { "type": "number" },
37
27
  "total_posts": { "type": "number" },
38
- "sentiment_score": { "type": "number", "description": "Net score (bullish-bearish) / total" }
28
+ "sentiment_score": { "type": "number" }
39
29
  },
40
30
  "required": ["bullish", "bearish", "neutral", "total_posts", "sentiment_score"]
41
31
  };
42
32
  }
43
33
 
44
- // --- THIS IS THE FIX ---
45
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
34
+ process(context) {
35
+ const socialPosts = context.social?.today || {};
46
36
  const counts = { bullish: 0, bearish: 0, neutral: 0 };
47
- let totalPosts = 0;
48
-
49
- // Get social data from Arg 1 (metaPayload)
50
- const socialDoc = dateStr.social; // This IS the map of posts
51
-
52
- if (!socialDoc || typeof socialDoc !== 'object') {
53
- this.result = { bullish: 0, bearish: 0, neutral: 0, total_posts: 0, sentiment_score: 0 };
54
- return;
55
- }
37
+ let total = 0;
56
38
 
57
- const postsArray = Object.values(socialDoc);
58
- totalPosts = postsArray.length;
59
-
60
- for (const post of postsArray) {
61
- // Use 'overallSentiment' and convert to lowercase
39
+ for (const postId in socialPosts) {
40
+ const post = socialPosts[postId];
62
41
  const sentiment = (post.sentiment?.overallSentiment || 'neutral').toLowerCase();
42
+
63
43
  if (counts.hasOwnProperty(sentiment)) {
64
44
  counts[sentiment]++;
45
+ total++;
46
+ } else {
47
+ counts.neutral++; // Default fallback
48
+ total++;
65
49
  }
66
50
  }
67
51
 
68
- const score = (totalPosts > 0)
69
- ? ((counts.bullish - counts.bearish) / totalPosts)
70
- : 0;
52
+ // Score: (Bullish - Bearish) / Total (-1 to 1 range)
53
+ const score = total > 0 ? (counts.bullish - counts.bearish) / total : 0;
71
54
 
72
55
  this.result = {
73
56
  bullish: counts.bullish,
74
57
  bearish: counts.bearish,
75
58
  neutral: counts.neutral,
76
- total_posts: totalPosts,
59
+ total_posts: total,
77
60
  sentiment_score: score
78
61
  };
79
62
  }
80
- // --- END FIX ---
81
-
82
- async getResult(fetchedDependencies) {
83
- return this.result;
84
- }
85
63
 
86
- reset() {
87
- this.result = {};
88
- }
64
+ async getResult() { return this.result; }
65
+ reset() { this.result = {}; }
89
66
  }
90
-
91
67
  module.exports = SocialSentimentAggregation;