aiden-shared-calculations-unified 1.0.83 → 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 -16
  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 +5 -5
  71. package/README.MD +0 -155
@@ -1,140 +1,140 @@
1
1
  /**
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.
2
+ * @fileoverview Calculation (Pass 2 - Meta) for social topic evolution.
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.topics' to 'post.sentiment.topics'.
9
+ * 6. Changed helper to read 'post.sentiment.overallSentiment'.
8
10
  */
9
11
  class SocialTopicInterestEvolution {
12
+
10
13
  constructor() {
11
- // { [topic]: { count: 0, history: [] } }
12
- this.topicData = new Map();
14
+ this.result = {};
13
15
  }
14
16
 
15
- // --- NEW ---
16
- /**
17
- * Statically defines all metadata for the manifest builder.
18
- */
19
17
  static getMetadata() {
20
18
  return {
21
19
  type: 'meta',
22
20
  rootDataDependencies: ['social'],
23
- isHistorical: false, // Stateful, not historical
24
- userType: 'n/a',
21
+ isHistorical: true,
22
+ userType: 'n'/'a',
25
23
  category: 'core_social'
26
24
  };
27
25
  }
28
26
 
29
- // --- NEW ---
30
- /**
31
- * Statically declare dependencies.
32
- */
33
27
  static getDependencies() {
34
28
  return [];
35
29
  }
36
30
 
37
- /**
38
- * Defines the output schema for this calculation.
39
- * @returns {object} JSON Schema object
40
- */
41
31
  static getSchema() {
42
32
  const topicSchema = {
43
33
  "type": "object",
44
- "description": "Daily and trended post count for a specific topic.",
45
34
  "properties": {
46
- "count": {
47
- "type": "number",
48
- "description": "Today's raw post count for this topic."
49
- },
50
- "trend_30d_pct": {
51
- "type": "number",
52
- "description": "Percentage change from the 30-day average post count."
53
- },
54
- "history_30d": {
55
- "type": "array",
56
- "description": "30-day history of post counts (today first).",
57
- "items": { "type": "number" }
58
- }
59
- },
60
- "required": ["count", "trend_30d_pct", "history_30d"]
35
+ "posts_1d_pct": { "type": "number" },
36
+ "posts_7d_pct": { "type": "number" },
37
+ "sentiment_1d": { "type": "number" },
38
+ "sentiment_7d": { "type": "number" }
39
+ }
61
40
  };
62
41
 
63
42
  return {
64
43
  "type": "object",
65
- "description": "Tracks the daily post count and 30-day trend for each extracted topic.",
66
- "patternProperties": {
67
- "^.*$": topicSchema // Topic name
68
- },
44
+ "description": "Calculates the evolution of post volume and sentiment for all social topics.",
45
+ "patternProperties": { "^.*$": topicSchema },
69
46
  "additionalProperties": topicSchema
70
47
  };
71
48
  }
72
49
 
73
- // --- REFACTORED ---
74
- /**
75
- * @param {string} dateStr - Today's date.
76
- * @param {object} rootData - The root data object.
77
- * @param {object} dependencies - db, logger, calculationUtils
78
- */
79
- async process(dateStr, rootData, dependencies) {
80
- const { calculationUtils } = dependencies;
81
-
82
- // 1. Load this metric's history from yesterday
83
- const yHistoryData = await calculationUtils.loadYesterdayData('social-topic-interest-evolution');
84
-
85
- if (yHistoryData) {
86
- for (const [topic, data] of Object.entries(yHistoryData)) {
87
- this.topicData.set(topic, {
88
- count: 0,
89
- history: data.history_30d || []
90
- });
91
- }
50
+ _getSentimentScore(sentimentString) {
51
+ const sentiment = sentimentString?.toLowerCase();
52
+ if (sentiment === 'bullish') return 1;
53
+ if (sentiment === 'bearish') return -1;
54
+ return 0;
55
+ }
56
+
57
+ // --- THIS IS THE FIX ---
58
+ async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
59
+
60
+ // Get social data from Arg 1 (metaPayload)
61
+ const socialDoc = dateStr.social; // This IS the map of posts
62
+ // Get the date from Arg 1
63
+ const todayDateStr = dateStr.date;
64
+
65
+ if (!socialDoc || typeof socialDoc !== 'object' || !todayDateStr) {
66
+ this.result = {};
67
+ return;
92
68
  }
93
-
94
- // 2. Process today's raw social data
95
- const todaySocialPosts = rootData.todaySocialPostInsights || {};
96
-
97
- for (const post of Object.values(todaySocialPosts)) {
98
- // Assume topics are extracted by the social loader
99
- // This now correctly reads the topics from the sentiment object
100
- const topics = post.sentiment?.topics || [];
69
+
70
+ const today = this._parseDate(todayDateStr);
71
+ const sevenDaysAgo = this._parseDate(todayDateStr);
72
+ sevenDaysAgo.setUTCDate(today.getUTCDate() - 7);
73
+
74
+ const topicStats = new Map();
75
+ let total1d = 0;
76
+ let total7d = 0;
77
+
78
+ const _init = (topic) => {
79
+ if (!topicStats.has(topic)) {
80
+ topicStats.set(topic, { c1: 0, c7: 0, s1: 0, s7: 0 });
81
+ }
82
+ };
83
+
84
+ for (const post of Object.values(socialDoc)) {
85
+ const postDate = this._parseDate(post.createdAt);
86
+ const is1d = postDate.toISOString().slice(0, 10) === todayDateStr;
87
+ const is7d = postDate >= sevenDaysAgo;
88
+
89
+ if (!is7d) continue;
90
+
91
+ const topics = post.sentiment?.topics || [];
92
+ if (topics.length === 0) continue;
93
+
94
+ const sentiment = this._getSentimentScore(post.sentiment?.overallSentiment);
95
+
101
96
  for (const topic of topics) {
102
- const normalizedTopic = topic.toLowerCase(); // Normalize
103
- if (!this.topicData.has(normalizedTopic)) {
104
- this.topicData.set(normalizedTopic, { count: 0, history: [] });
97
+ _init(topic);
98
+ const stats = topicStats.get(topic);
99
+
100
+ if (is7d) {
101
+ stats.c7++;
102
+ stats.s7 += sentiment;
103
+ total7d++;
104
+ }
105
+ if (is1d) {
106
+ stats.c1++;
107
+ stats.s1 += sentiment;
108
+ total1d++;
105
109
  }
106
- this.topicData.get(normalizedTopic).count++;
107
110
  }
108
111
  }
109
- }
110
112
 
111
- async getResult() {
112
113
  const result = {};
113
- for (const [topic, data] of this.topicData.entries()) {
114
- const history = data.history;
115
- const todayCount = data.count;
116
-
117
- const newHistory = [todayCount, ...history].slice(0, 30);
118
-
119
- let trend_30d_pct = 0;
120
- if (history.length > 0) {
121
- const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
122
- if (avg_30d > 0) {
123
- trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
124
- }
125
- }
126
-
114
+ for (const [topic, stats] of topicStats.entries()) {
127
115
  result[topic] = {
128
- count: todayCount,
129
- trend_30d_pct: trend_30d_pct,
130
- history_30d: newHistory
116
+ posts_1d_pct: (total1d > 0) ? (stats.c1 / total1d) * 100 : 0,
117
+ posts_7d_pct: (total7d > 0) ? (stats.c7 / total7d) * 100 : 0,
118
+ sentiment_1d: (stats.c1 > 0) ? (stats.s1 / stats.c1) : 0,
119
+ sentiment_7d: (stats.c7 > 0) ? (stats.s7 / stats.c7) : 0,
131
120
  };
132
121
  }
133
- return result;
122
+
123
+ this.result = result;
124
+ }
125
+ // --- END FIX ---
126
+
127
+ _parseDate(dateStr) {
128
+ // Helper to ensure UTC parsing
129
+ return new Date(String(dateStr).slice(0, 10) + 'T00:00:00Z');
130
+ }
131
+
132
+ async getResult(fetchedDependencies) {
133
+ return this.result;
134
134
  }
135
135
 
136
136
  reset() {
137
- this.topicData.clear();
137
+ this.result = {};
138
138
  }
139
139
  }
140
140
 
@@ -1,120 +1,136 @@
1
1
  /**
2
- * @fileoverview Social Calculation (Pass 1)
3
- *
4
- * This metric answers: "What is the sentiment breakdown
5
- * (bullish, bearish) for each topic?"
6
- *
7
- * Creates a matrix of Topic <-> Sentiment.
2
+ * @fileoverview Calculation (Pass 2 - Meta) for social topic/sentiment.
3
+ * --- FIX ---
4
+ * 1. Changed 'process' signature to 5-arg 'meta' standard.
5
+ * 2. Removed 'calculationUtils.loadInstrumentMappings()' as it's
6
+ * not available in the test harness 'meta' signature.
7
+ * 3. Logic now uses 'config.instrumentToTicker' (Arg 4)
8
+ * which IS provided by the worker.
9
+ * 4. Changed 'socialDoc.posts' to 'socialDoc' (Arg 1).
10
+ * 5. Changed 'post.sentiment.label' to 'post.sentiment.overallSentiment'.
11
+ * 6. Changed 'post.topics' to 'post.sentiment.topics'.
12
+ * 7. Changed 'post.instrumentIds' to 'post.tickers'.
8
13
  */
14
+
9
15
  class SocialTopicSentimentMatrix {
16
+
10
17
  constructor() {
11
- // { [topic]: { bullish: 0, bearish: 0, neutral: 0 } }
12
- this.matrix = new Map();
18
+ this.result = {};
13
19
  }
14
20
 
15
- // --- NEW ---
16
- /**
17
- * Statically defines all metadata for the manifest builder.
18
- */
19
21
  static getMetadata() {
20
22
  return {
21
23
  type: 'meta',
22
24
  rootDataDependencies: ['social'],
23
25
  isHistorical: false,
24
- userType: 'n/a',
26
+ userType: 'n'/'a',
25
27
  category: 'core_social'
26
28
  };
27
29
  }
28
30
 
29
- // --- NEW ---
30
- /**
31
- * Statically declare dependencies.
32
- */
33
31
  static getDependencies() {
34
32
  return [];
35
33
  }
36
34
 
37
- /**
38
- * Defines the output schema for this calculation.
39
- * @returns {object} JSON Schema object
40
- */
41
35
  static getSchema() {
42
- const sentimentSchema = {
36
+ const schema = {
43
37
  "type": "object",
44
38
  "properties": {
45
- "bullish": { "type": "number" },
46
- "bearish": { "type": "number" },
47
- "neutral": { "type": "number" },
48
- "net_sentiment": { "type": "number" }
49
- },
50
- "required": ["bullish", "bearish", "neutral", "net_sentiment"]
39
+ "avg_sentiment": { "type": "number" },
40
+ "post_count": { "type": "number" }
41
+ }
51
42
  };
52
43
 
53
44
  return {
54
45
  "type": "object",
55
- "description": "Calculates the sentiment breakdown (bullish, bearish, neutral) for each topic.",
56
- "patternProperties": {
57
- "^.*$": sentimentSchema // Topic name
46
+ "description": "Calculates the average sentiment and post count for all topics and all instruments.",
47
+ "properties": {
48
+ "by_topic": { "type": "object", "patternProperties": { "^.*$": schema }, "additionalProperties": schema },
49
+ "by_instrument": { "type": "object", "patternProperties": { "^.*$": schema }, "additionalProperties": schema }
58
50
  },
59
- "additionalProperties": sentimentSchema
51
+ "required": ["by_topic", "by_instrument"]
60
52
  };
61
53
  }
62
54
 
63
- _initTopic(topic) {
64
- if (!this.matrix.has(topic)) {
65
- this.matrix.set(topic, {
66
- bullish: 0,
67
- bearish: 0,
68
- neutral: 0
69
- });
55
+ _getSentimentScore(sentimentString) {
56
+ const sentiment = sentimentString?.toLowerCase();
57
+ if (sentiment === 'bullish') return 1;
58
+ if (sentiment === 'bearish') return -1;
59
+ return 0;
60
+ }
61
+
62
+ _init(map, key) {
63
+ if (!map.has(key)) {
64
+ map.set(key, { score_sum: 0, count: 0 });
70
65
  }
71
66
  }
72
67
 
73
- // --- REFACTORED ---
74
- /**
75
- * @param {string} dateStr - Today's date.
76
- * @param {object} rootData - The root data object.
77
- * @param {object} dependencies - db, logger, calculationUtils
78
- */
79
- async process(dateStr, rootData, dependencies) {
80
- const { calculationUtils } = dependencies;
81
- const todaySocialPosts = rootData.todaySocialPostInsights || {};
82
-
83
- for (const post of Object.values(todaySocialPosts)) {
68
+ // --- THIS IS THE FIX ---
69
+ async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
70
+ // Get mappings from Arg 4 (config)
71
+ const { instrumentToTicker } = config;
72
+
73
+ // Get social data from Arg 1 (dateStr is metaPayload)
74
+ const socialDoc = dateStr.social;
75
+
76
+ if (!socialDoc || typeof socialDoc !== 'object' || !instrumentToTicker) {
77
+ this.result = { by_topic: {}, by_instrument: {} };
78
+ return;
79
+ }
80
+
81
+ const topicMap = new Map();
82
+ const instrumentMap = new Map();
83
+
84
+ for (const post of Object.values(socialDoc)) {
85
+ const sentiment = this._getSentimentScore(post.sentiment?.overallSentiment);
86
+
87
+ // 1. Process topics
84
88
  const topics = post.sentiment?.topics || [];
85
- // Use the structured sentiment object
86
- const sentiment = post.sentiment?.overallSentiment?.toLowerCase() || 'neutral';
87
-
88
89
  for (const topic of topics) {
89
- const normalizedTopic = topic.toLowerCase(); // Normalize
90
- this._initTopic(normalizedTopic);
91
- const data = this.matrix.get(normalizedTopic);
92
-
93
- if (sentiment === 'bullish') {
94
- data.bullish++;
95
- } else if (sentiment === 'bearish') {
96
- data.bearish++;
97
- } else {
98
- data.neutral++;
99
- }
90
+ this._init(topicMap, topic);
91
+ const data = topicMap.get(topic);
92
+ data.score_sum += sentiment;
93
+ data.count++;
94
+ }
95
+
96
+ // 2. Process instruments (tickers)
97
+ const tickers = post.tickers || [];
98
+ for (const ticker of tickers) {
99
+ if (!ticker) continue;
100
+
101
+ this._init(instrumentMap, ticker);
102
+ const data = instrumentMap.get(ticker);
103
+ data.score_sum += sentiment;
104
+ data.count++;
100
105
  }
101
106
  }
102
- }
103
107
 
104
- async getResult() {
105
- const result = {};
106
- for (const [topic, data] of this.matrix.entries()) {
107
- result[topic] = {
108
- ...data,
109
- // Net Sentiment = (Bullish - Bearish)
110
- net_sentiment: data.bullish - data.bearish
108
+ const result = { by_topic: {}, by_instrument: {} };
109
+
110
+ for (const [topic, data] of topicMap.entries()) {
111
+ result.by_topic[topic] = {
112
+ avg_sentiment: (data.count > 0) ? (data.score_sum / data.count) : 0,
113
+ post_count: data.count
111
114
  };
112
115
  }
113
- return result;
116
+
117
+ for (const [ticker, data] of instrumentMap.entries()) {
118
+ result.by_instrument[ticker] = {
119
+ avg_sentiment: (data.count > 0) ? (data.score_sum / data.count) : 0,
120
+ post_count: data.count
121
+ };
122
+ }
123
+
124
+ this.result = result;
125
+ }
126
+ // --- END FIX ---
127
+
128
+ async getResult(fetchedDependencies) {
129
+ return this.result;
114
130
  }
115
131
 
116
132
  reset() {
117
- this.matrix.clear();
133
+ this.result = {};
118
134
  }
119
135
  }
120
136