aiden-shared-calculations-unified 1.0.64 → 1.0.66

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 (89) hide show
  1. package/README.MD +1 -1
  2. package/calculations/activity/historical/activity_by_pnl_status.js +33 -0
  3. package/calculations/activity/historical/daily_asset_activity.js +42 -0
  4. package/calculations/activity/historical/daily_user_activity_tracker.js +37 -0
  5. package/calculations/activity/historical/speculator_adjustment_activity.js +26 -0
  6. package/calculations/asset_metrics/asset_position_size.js +36 -0
  7. package/calculations/backtests/strategy-performance.js +41 -0
  8. package/calculations/behavioural/historical/asset_crowd_flow.js +124 -127
  9. package/calculations/behavioural/historical/drawdown_response.js +113 -35
  10. package/calculations/behavioural/historical/dumb-cohort-flow.js +191 -171
  11. package/calculations/behavioural/historical/gain_response.js +113 -34
  12. package/calculations/behavioural/historical/historical_performance_aggregator.js +63 -48
  13. package/calculations/behavioural/historical/in_loss_asset_crowd_flow.js +159 -63
  14. package/calculations/behavioural/historical/in_profit_asset_crowd_flow.js +159 -64
  15. package/calculations/behavioural/historical/paper_vs_diamond_hands.js +86 -19
  16. package/calculations/behavioural/historical/position_count_pnl.js +91 -39
  17. package/calculations/behavioural/historical/smart-cohort-flow.js +192 -172
  18. package/calculations/behavioural/historical/smart_money_flow.js +160 -151
  19. package/calculations/capital_flow/historical/crowd-cash-flow-proxy.js +95 -89
  20. package/calculations/capital_flow/historical/deposit_withdrawal_percentage.js +88 -81
  21. package/calculations/capital_flow/historical/new_allocation_percentage.js +75 -26
  22. package/calculations/capital_flow/historical/reallocation_increase_percentage.js +73 -32
  23. package/calculations/insights/daily_buy_sell_sentiment_count.js +47 -32
  24. package/calculations/insights/daily_total_positions_held.js +28 -24
  25. package/calculations/insights/historical/daily_bought_vs_sold_count.js +101 -36
  26. package/calculations/insights/historical/daily_ownership_delta.js +95 -32
  27. package/calculations/meta/capital_deployment_strategy.js +78 -110
  28. package/calculations/meta/capital_liquidation_performance.js +114 -111
  29. package/calculations/meta/cash-flow-deployment.js +114 -107
  30. package/calculations/meta/cash-flow-liquidation.js +114 -107
  31. package/calculations/meta/crowd_sharpe_ratio_proxy.js +94 -54
  32. package/calculations/meta/negative_expectancy_cohort_flow.js +185 -177
  33. package/calculations/meta/positive_expectancy_cohort_flow.js +186 -181
  34. package/calculations/meta/profit_cohort_divergence.js +83 -59
  35. package/calculations/meta/shark_attack_signal.js +91 -39
  36. package/calculations/meta/smart-dumb-divergence-index.js +114 -98
  37. package/calculations/meta/smart_dumb_divergence_index_v2.js +109 -98
  38. package/calculations/meta/social-predictive-regime-state.js +76 -155
  39. package/calculations/meta/social-topic-driver-index.js +74 -127
  40. package/calculations/meta/user_expectancy_score.js +83 -31
  41. package/calculations/pnl/asset_pnl_status.js +120 -31
  42. package/calculations/pnl/average_daily_pnl_all_users.js +42 -27
  43. package/calculations/pnl/average_daily_pnl_per_sector.js +84 -26
  44. package/calculations/pnl/average_daily_pnl_per_stock.js +71 -29
  45. package/calculations/pnl/average_daily_position_pnl.js +49 -21
  46. package/calculations/pnl/historical/profitability_migration.js +81 -35
  47. package/calculations/pnl/historical/user_profitability_tracker.js +107 -104
  48. package/calculations/pnl/pnl_distribution_per_stock.js +65 -45
  49. package/calculations/pnl/profitability_ratio_per_stock.js +78 -21
  50. package/calculations/pnl/profitability_skew_per_stock.js +86 -31
  51. package/calculations/pnl/profitable_and_unprofitable_status.js +45 -45
  52. package/calculations/sanity/users_processed.js +24 -1
  53. package/calculations/sectors/historical/diversification_pnl.js +104 -42
  54. package/calculations/sectors/historical/sector_rotation.js +94 -45
  55. package/calculations/sectors/total_long_per_sector.js +55 -20
  56. package/calculations/sectors/total_short_per_sector.js +55 -20
  57. package/calculations/sentiment/historical/crowd_conviction_score.js +233 -53
  58. package/calculations/short_and_long_stats/long_position_per_stock.js +50 -14
  59. package/calculations/short_and_long_stats/sentiment_per_stock.js +76 -19
  60. package/calculations/short_and_long_stats/short_position_per_stock.js +50 -13
  61. package/calculations/short_and_long_stats/total_long_figures.js +34 -13
  62. package/calculations/short_and_long_stats/total_short_figures.js +34 -14
  63. package/calculations/socialPosts/social-asset-posts-trend.js +96 -29
  64. package/calculations/socialPosts/social-top-mentioned-words.js +95 -74
  65. package/calculations/socialPosts/social-topic-interest-evolution.js +92 -29
  66. package/calculations/socialPosts/social-topic-sentiment-matrix.js +70 -78
  67. package/calculations/socialPosts/social-word-mentions-trend.js +96 -38
  68. package/calculations/socialPosts/social_activity_aggregation.js +106 -77
  69. package/calculations/socialPosts/social_sentiment_aggregation.js +115 -86
  70. package/calculations/speculators/distance_to_stop_loss_per_leverage.js +82 -43
  71. package/calculations/speculators/distance_to_tp_per_leverage.js +81 -42
  72. package/calculations/speculators/entry_distance_to_sl_per_leverage.js +80 -44
  73. package/calculations/speculators/entry_distance_to_tp_per_leverage.js +81 -44
  74. package/calculations/speculators/historical/risk_appetite_change.js +89 -32
  75. package/calculations/speculators/historical/tsl_effectiveness.js +57 -47
  76. package/calculations/speculators/holding_duration_per_asset.js +83 -23
  77. package/calculations/speculators/leverage_per_asset.js +68 -19
  78. package/calculations/speculators/leverage_per_sector.js +86 -25
  79. package/calculations/speculators/risk_reward_ratio_per_asset.js +82 -28
  80. package/calculations/speculators/speculator_asset_sentiment.js +100 -48
  81. package/calculations/speculators/speculator_danger_zone.js +101 -33
  82. package/calculations/speculators/stop_loss_distance_by_sector_short_long_breakdown.js +93 -66
  83. package/calculations/speculators/stop_loss_distance_by_ticker_short_long_breakdown.js +94 -47
  84. package/calculations/speculators/stop_loss_per_asset.js +94 -26
  85. package/calculations/speculators/take_profit_per_asset.js +95 -27
  86. package/calculations/speculators/tsl_per_asset.js +77 -23
  87. package/package.json +1 -1
  88. package/utils/price_data_provider.js +142 -142
  89. package/utils/sector_mapping_provider.js +74 -74
@@ -1,102 +1,123 @@
1
1
  /**
2
- * @fileoverview Finds the top mentioned words in all posts for the day,
3
- * filtering out common stop words, and calculates the trend from yesterday.
4
- * This calculation ONLY uses the social post insights context.
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.
5
9
  */
6
-
7
10
  class SocialTopMentionedWords {
8
11
  constructor() {
9
- this.todayWordCounts = {};
10
- this.yesterdayWordCounts = {};
11
- this.processed = false;
12
-
13
- // A small, representative list of stop words.
14
- // In a real implementation, this list would be much larger.
15
- this.stopWords = new Set([
16
- 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
17
- 'have', 'has', 'had', 'do', 'does', 'did', 'but', 'if', 'or', 'and',
18
- 'of', 'at', 'by', 'for', 'with', 'about', 'to', 'from', 'in', 'on',
19
- 'it', 'i', 'me', 'my', 'you', 'your', 'he', 'she', 'we', 'they', 'them',
20
- 'this', 'that', 'these', 'those', 'what', 'which', 'who', 'whom',
21
- 'not', 'will', 'just', 'so', 'up', 'out', 'no', 'yes', 'all', 'any'
22
- ]);
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']);
23
16
  }
24
17
 
25
18
  /**
26
- * Helper to process posts and populate a word count map.
27
- * @param {object} socialPostInsights - Map of { [postId]: postData }
28
- * @param {object} targetCountMap - The object to store word counts.
19
+ * Defines the output schema for this calculation.
20
+ * @returns {object} JSON Schema object
29
21
  */
30
- _countWords(socialPostInsights, targetCountMap) {
31
- if (!socialPostInsights) return;
32
- const posts = Object.values(socialPostInsights);
33
-
34
- for (const post of posts) {
35
- const text = post.fullText?.toLowerCase() || '';
36
- if (!text) continue;
37
-
38
- // Get all words 3+ chars long
39
- const words = text.match(/\b[a-z]{3,}\b/g) || [];
22
+ static getSchema() {
23
+ const wordSchema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "word": { "type": "string" },
27
+ "count": { "type": "number" },
28
+ "trend_30d_pct": { "type": "number" },
29
+ "history_30d": { "type": "array", "items": { "type": "number" } }
30
+ },
31
+ "required": ["word", "count", "trend_30d_pct", "history_30d"]
32
+ };
40
33
 
41
- for (const word of words) {
42
- if (!this.stopWords.has(word)) {
43
- targetCountMap[word] = (targetCountMap[word] || 0) + 1;
44
- }
45
- }
46
- }
34
+ return {
35
+ "type": "array",
36
+ "description": "Top 50 most mentioned words (non-stop words) with their 30-day trend.",
37
+ "items": wordSchema
38
+ };
47
39
  }
48
40
 
49
41
  /**
50
- * @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
51
- * @param {object} yesterdaySocialPostInsights - Map of { [postId]: postData } for yesterday.
42
+ * @param {string} dateStr - Today's date.
43
+ * @param {object} dependencies - db, logger.
44
+ * @param {object} config - Computation config.
45
+ * @param {object} fetchedDependencies - (UNUSED) In-memory results.
52
46
  */
53
- async process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights) {
47
+ async process(dateStr, dependencies, config, fetchedDependencies) {
48
+ const { db, logger, calculationUtils } = dependencies;
49
+
50
+ // 1. Load this metric's history from yesterday
51
+ // Note: History is stored by word
52
+ const yHistoryData = await calculationUtils.loadYesterdayData('social-top-mentioned-words-history'); // Fictional helper
54
53
 
55
- if (this.processed) {
56
- return;
54
+ if (yHistoryData) {
55
+ for (const [word, data] of Object.entries(yHistoryData)) {
56
+ this.wordCounts.set(word, {
57
+ count: 0,
58
+ history: data.history_30d || []
59
+ });
60
+ }
57
61
  }
58
- this.processed = true;
59
-
60
- // Process today's posts
61
- this._countWords(todaySocialPostInsights, this.todayWordCounts);
62
62
 
63
- // Process yesterday's posts for trend calculation
64
- this._countWords(yesterdaySocialPostInsights, this.yesterdayWordCounts);
65
- }
63
+ // 2. Process today's raw social data
64
+ const todaySocialPosts = await calculationUtils.loadSocialData(dateStr);
66
65
 
67
- async getResult() {
68
- if (Object.keys(this.todayWordCounts).length === 0) {
69
- return {};
66
+ for (const post of todaySocialPosts) {
67
+ const text = (post.content || '').toLowerCase().replace(/[^a-z\s]/g, '');
68
+ const words = text.split(/\s+/);
69
+
70
+ for (const word of words) {
71
+ if (word.length > 2 && !this.stopWords.has(word)) {
72
+ if (!this.wordCounts.has(word)) {
73
+ this.wordCounts.set(word, { count: 0, history: [] });
74
+ }
75
+ this.wordCounts.get(word).count++;
76
+ }
77
+ }
70
78
  }
79
+ }
71
80
 
72
- const totalTodayWords = Object.values(this.todayWordCounts).reduce((a, b) => a + b, 0);
73
-
74
- const topWords = Object.entries(this.todayWordCounts)
75
- .sort(([, countA], [, countB]) => countB - countA)
76
- .slice(0, 50); // Return the Top 50
81
+ async getResult() {
82
+ const allWords = [];
83
+
84
+ for (const [word, data] of this.wordCounts.entries()) {
85
+ const history = data.history;
86
+ const todayCount = data.count;
87
+
88
+ // Don't include words that weren't mentioned today
89
+ if (todayCount === 0) continue;
77
90
 
78
- const result = topWords.map(([term, count]) => {
79
- const yesterdayCount = this.yesterdayWordCounts[term] || 0;
80
- const trend = count - yesterdayCount;
81
- const percent = (count / totalTodayWords) * 100;
91
+ const newHistory = [todayCount, ...history].slice(0, 30);
82
92
 
83
- return {
84
- term,
85
- count,
86
- percent: parseFloat(percent.toFixed(2)),
87
- trend
88
- };
89
- });
93
+ let trend_30d_pct = 0;
94
+ if (history.length > 0) {
95
+ const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
96
+ if (avg_30d > 0) {
97
+ trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
98
+ }
99
+ }
90
100
 
91
- // Returns an array like:
92
- // [ { term: "btc", count: 150, percent: 5.2, trend: 25 }, ... ]
93
- return { top_words: result };
101
+ allWords.push({
102
+ word: word,
103
+ count: todayCount,
104
+ trend_30d_pct: trend_30d_pct,
105
+ history_30d: newHistory
106
+ });
107
+ }
108
+
109
+ // Sort by today's count and take top 50
110
+ allWords.sort((a, b) => b.count - a.count);
111
+
112
+ // The *full* result (for history) should be saved to a different
113
+ // doc ('social-top-mentioned-words-history') by the computation system.
114
+
115
+ // Return only the top 50 for the API.
116
+ return allWords.slice(0, 50);
94
117
  }
95
118
 
96
119
  reset() {
97
- this.todayWordCounts = {};
98
- this.yesterdayWordCounts = {};
99
- this.processed = false;
120
+ this.wordCounts.clear();
100
121
  }
101
122
  }
102
123
 
@@ -1,53 +1,116 @@
1
1
  /**
2
- * @fileoverview Aggregates counts of all topics extracted by the AI
3
- * from social posts for the day. Used to build a "Topic Interest Area" chart.
4
- * This calculation ONLY uses the social post insights context.
2
+ * @fileoverview Social Calculation (Pass 1)
3
+ *
4
+ * This metric answers: "What is the daily count of
5
+ * posts for each topic extracted from social media?"
6
+ *
7
+ * This is a stateful calculation.
5
8
  */
6
-
7
9
  class SocialTopicInterestEvolution {
8
10
  constructor() {
9
- this.topicCounts = {};
10
- this.processed = false;
11
+ // { [topic]: { count: 0, history: [] } }
12
+ this.topicData = new Map();
11
13
  }
12
14
 
13
15
  /**
14
- * @param {null} todayPortfolio - Not used.
15
- * ... (other params not used) ...
16
- * @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
17
- * @param {object} yesterdaySocialPostInsights - Not used.
16
+ * Defines the output schema for this calculation.
17
+ * @returns {object} JSON Schema object
18
18
  */
19
- async process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights) {
20
-
21
- if (this.processed || !todaySocialPostInsights) {
22
- return;
23
- }
24
- this.processed = true;
19
+ static getSchema() {
20
+ const topicSchema = {
21
+ "type": "object",
22
+ "description": "Daily and trended post count for a specific topic.",
23
+ "properties": {
24
+ "count": {
25
+ "type": "number",
26
+ "description": "Today's raw post count for this topic."
27
+ },
28
+ "trend_30d_pct": {
29
+ "type": "number",
30
+ "description": "Percentage change from the 30-day average post count."
31
+ },
32
+ "history_30d": {
33
+ "type": "array",
34
+ "description": "30-day history of post counts (today first).",
35
+ "items": { "type": "number" }
36
+ }
37
+ },
38
+ "required": ["count", "trend_30d_pct", "history_30d"]
39
+ };
25
40
 
26
- const posts = Object.values(todaySocialPostInsights);
41
+ return {
42
+ "type": "object",
43
+ "description": "Tracks the daily post count and 30-day trend for each extracted topic.",
44
+ "patternProperties": {
45
+ "^.*$": topicSchema // Topic name
46
+ },
47
+ "additionalProperties": topicSchema
48
+ };
49
+ }
27
50
 
28
- for (const post of posts) {
29
- // Topics are in post.sentiment.topics
30
- const topics = post.sentiment?.topics;
31
- if (!topics || !Array.isArray(topics)) continue;
51
+ /**
52
+ * @param {string} dateStr - Today's date.
53
+ * @param {object} dependencies - db, logger.
54
+ * @param {object} config - Computation config.
55
+ * @param {object} fetchedDependencies - (UNUSED) In-memory results.
56
+ */
57
+ async process(dateStr, dependencies, config, fetchedDependencies) {
58
+ const { db, logger, calculationUtils } = dependencies;
59
+
60
+ // 1. Load this metric's history from yesterday
61
+ const yHistoryData = await calculationUtils.loadYesterdayData('social-topic-interest-evolution');
32
62
 
63
+ if (yHistoryData) {
64
+ for (const [topic, data] of Object.entries(yHistoryData)) {
65
+ this.topicData.set(topic, {
66
+ count: 0,
67
+ history: data.history_30d || []
68
+ });
69
+ }
70
+ }
71
+
72
+ // 2. Process today's raw social data
73
+ const todaySocialPosts = await calculationUtils.loadSocialData(dateStr);
74
+
75
+ for (const post of todaySocialPosts) {
76
+ // Assume topics are extracted by the social loader
77
+ const topics = post.topics || [];
33
78
  for (const topic of topics) {
34
- // Normalize topic for consistent aggregation
35
- const normalizedTopic = String(topic).toLowerCase().trim();
36
- if (normalizedTopic) {
37
- this.topicCounts[normalizedTopic] = (this.topicCounts[normalizedTopic] || 0) + 1;
79
+ if (!this.topicData.has(topic)) {
80
+ this.topicData.set(topic, { count: 0, history: [] });
38
81
  }
82
+ this.topicData.get(topic).count++;
39
83
  }
40
84
  }
41
85
  }
42
86
 
43
87
  async getResult() {
44
- // Returns an object like: { "earnings": 45, "cpi": 12, "fed": 22 }
45
- return this.topicCounts;
88
+ const result = {};
89
+ for (const [topic, data] of this.topicData.entries()) {
90
+ const history = data.history;
91
+ const todayCount = data.count;
92
+
93
+ const newHistory = [todayCount, ...history].slice(0, 30);
94
+
95
+ let trend_30d_pct = 0;
96
+ if (history.length > 0) {
97
+ const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
98
+ if (avg_30d > 0) {
99
+ trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
100
+ }
101
+ }
102
+
103
+ result[topic] = {
104
+ count: todayCount,
105
+ trend_30d_pct: trend_30d_pct,
106
+ history_30d: newHistory
107
+ };
108
+ }
109
+ return result;
46
110
  }
47
111
 
48
112
  reset() {
49
- this.topicCounts = {};
50
- this.processed = false;
113
+ this.topicData.clear();
51
114
  }
52
115
  }
53
116
 
@@ -1,104 +1,96 @@
1
1
  /**
2
- * @fileoverview NEW CALCULATION (PASS 1)
3
- * Aggregates all social post data for the day into a detailed matrix.
4
- * For each ticker, it maps each discussed topic to its aggregated
5
- * sentiment (Bullish/Bearish/Neutral) and a "Conviction Score"
6
- * derived from likes and comments.
2
+ * @fileoverview Social Calculation (Pass 1)
7
3
  *
8
- * This is a "socialPosts" category calculation, so it runs once per day.
4
+ * This metric answers: "What is the sentiment breakdown
5
+ * (bullish, bearish) for each topic?"
6
+ *
7
+ * Creates a matrix of Topic <-> Sentiment.
9
8
  */
10
-
11
9
  class SocialTopicSentimentMatrix {
12
10
  constructor() {
13
- // The main data structure.
14
- // Format: { [ticker]: { [topic]: { ...stats } } }
15
- this.matrix = {};
16
-
17
- // Flag to ensure this runs only once per day
18
- this.processed = false;
11
+ // { [topic]: { bullish: 0, bearish: 0, neutral: 0 } }
12
+ this.matrix = new Map();
19
13
  }
20
14
 
21
15
  /**
22
- * @param {null} todayPortfolio - Not used.
23
- * @param {null} yesterdayPortfolio - Not used.
24
- * @param {null} userId - Not used.
25
- * @param {object} context - Shared context.
26
- * @param {object} todayInsights - Not used.
27
- * @param {object} yesterdayInsights - Not used.
28
- * @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
29
- * @param {object} yesterdaySocialPostInsights - Not used.
16
+ * Defines the output schema for this calculation.
17
+ * @returns {object} JSON Schema object
30
18
  */
31
- async process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights) {
32
-
33
- // Run only once per day
34
- if (this.processed || !todaySocialPostInsights) {
35
- return;
36
- }
37
- this.processed = true;
38
-
39
- const posts = Object.values(todaySocialPostInsights);
19
+ static getSchema() {
20
+ const sentimentSchema = {
21
+ "type": "object",
22
+ "properties": {
23
+ "bullish": { "type": "number" },
24
+ "bearish": { "type": "number" },
25
+ "neutral": { "type": "number" },
26
+ "net_sentiment": { "type": "number" }
27
+ },
28
+ "required": ["bullish", "bearish", "neutral", "net_sentiment"]
29
+ };
40
30
 
41
- for (const post of posts) {
42
- if (!post || !post.tickers || !post.sentiment) continue;
31
+ return {
32
+ "type": "object",
33
+ "description": "Calculates the sentiment breakdown (bullish, bearish, neutral) for each topic.",
34
+ "patternProperties": {
35
+ "^.*$": sentimentSchema // Topic name
36
+ },
37
+ "additionalProperties": sentimentSchema
38
+ };
39
+ }
43
40
 
44
- const tickers = post.tickers;
45
- const sentiment = post.sentiment.overallSentiment || 'Neutral';
46
-
47
- // Use a default topic if Gemini found none
48
- let topics = post.sentiment.topics || [];
49
- if (topics.length === 0) {
50
- topics.push('untagged');
51
- }
41
+ _initTopic(topic) {
42
+ if (!this.matrix.has(topic)) {
43
+ this.matrix.set(topic, {
44
+ bullish: 0,
45
+ bearish: 0,
46
+ neutral: 0
47
+ });
48
+ }
49
+ }
52
50
 
53
- // Calculate conviction (e.g., comments are 2x as valuable as likes)
54
- const convictionScore = (post.likeCount || 0) + ((post.commentCount || 0) * 2);
51
+ /**
52
+ * @param {string} dateStr - Today's date.
53
+ * @param {object} dependencies - db, logger.
54
+ * @param {object} config - Computation config.
55
+ * @param {object} fetchedDependencies - (UNUSED) In-memory results.
56
+ */
57
+ async process(dateStr, dependencies, config, fetchedDependencies) {
58
+ const { calculationUtils } = dependencies;
59
+ const todaySocialPosts = await calculationUtils.loadSocialData(dateStr);
55
60
 
56
- for (const ticker of tickers) {
57
- if (!ticker) continue;
61
+ for (const post of todaySocialPosts) {
62
+ const topics = post.topics || [];
63
+ const sentiment = post.sentiment || 'neutral'; // 'bullish', 'bearish', 'neutral'
64
+
65
+ for (const topic of topics) {
66
+ this._initTopic(topic);
67
+ const data = this.matrix.get(topic);
58
68
 
59
- for (const topic of topics) {
60
- const topicLower = topic.toLowerCase();
61
-
62
- // Initialize ticker map
63
- if (!this.matrix[ticker]) {
64
- this.matrix[ticker] = {};
65
- }
66
- // Initialize topic map
67
- if (!this.matrix[ticker][topicLower]) {
68
- this.matrix[ticker][topicLower] = {
69
- bullishPosts: 0,
70
- bearishPosts: 0,
71
- neutralPosts: 0,
72
- totalPosts: 0,
73
- convictionScore: 0
74
- };
75
- }
76
-
77
- // Aggregate data into the bucket
78
- const bucket = this.matrix[ticker][topicLower];
79
- bucket.totalPosts++;
80
- bucket.convictionScore += convictionScore;
81
-
82
- if (sentiment === 'Bullish') {
83
- bucket.bullishPosts++;
84
- } else if (sentiment === 'Bearish') {
85
- bucket.bearishPosts++;
86
- } else {
87
- bucket.neutralPosts++;
88
- }
69
+ if (sentiment === 'bullish') {
70
+ data.bullish++;
71
+ } else if (sentiment === 'bearish') {
72
+ data.bearish++;
73
+ } else {
74
+ data.neutral++;
89
75
  }
90
76
  }
91
77
  }
92
78
  }
93
79
 
94
80
  async getResult() {
95
- // This entire matrix will be the result
96
- return this.matrix;
81
+ const result = {};
82
+ for (const [topic, data] of this.matrix.entries()) {
83
+ result[topic] = {
84
+ ...data,
85
+ // Net Sentiment = (Bullish - Bearish)
86
+ net_sentiment: data.bullish - data.bearish
87
+ };
88
+ }
89
+ return result;
97
90
  }
98
91
 
99
92
  reset() {
100
- this.matrix = {};
101
- this.processed = false;
93
+ this.matrix.clear();
102
94
  }
103
95
  }
104
96