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.
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,141 +1,124 @@
1
1
  /**
2
- * @fileoverview Social Calculation (Pass 1)
3
- *
4
- * This metric answers: "How many social media posts
5
- * were made today for each asset (ticker)?"
6
- *
7
- * This is a stateful calculation that needs to read its
8
- * own 30-day history from Firestore to calculate trend.
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.
9
10
  */
11
+
10
12
  class SocialAssetPostsTrend {
13
+
11
14
  constructor() {
12
- // { [ticker]: { count: 0, history: [] } }
13
- this.assetData = new Map();
15
+ this.result = {};
14
16
  }
15
17
 
16
- // --- NEW ---
17
- /**
18
- * Statically defines all metadata for the manifest builder.
19
- */
20
18
  static getMetadata() {
21
19
  return {
22
20
  type: 'meta',
23
- rootDataDependencies: ['social'], // Needs social posts
24
- isHistorical: false, // It's stateful, but reads its *own* history
21
+ rootDataDependencies: ['social'],
22
+ isHistorical: true,
25
23
  userType: 'n/a',
26
24
  category: 'core_social'
27
25
  };
28
26
  }
29
27
 
30
- // --- NEW ---
31
- /**
32
- * Statically declare dependencies.
33
- */
34
28
  static getDependencies() {
35
29
  return [];
36
30
  }
37
31
 
38
- /**
39
- * Defines the output schema for this calculation.
40
- * @returns {object} JSON Schema object
41
- */
42
32
  static getSchema() {
43
33
  const tickerSchema = {
44
34
  "type": "object",
45
- "description": "Daily and trended post count for a specific asset.",
46
35
  "properties": {
47
- "count": {
48
- "type": "number",
49
- "description": "Today's raw post count."
50
- },
51
- "trend_30d_pct": {
52
- "type": "number",
53
- "description": "Percentage change from the 30-day average post count."
54
- },
55
- "history_30d": {
56
- "type": "array",
57
- "description": "30-day history of post counts (today first).",
58
- "items": { "type": "number" }
59
- }
36
+ "posts_1d_pct": { "type": "number" },
37
+ "posts_7d_pct": { "type": "number" },
38
+ "trend_ratio": { "type": ["number", "null"] }
60
39
  },
61
- "required": ["count", "trend_30d_pct", "history_30d"]
40
+ "required": ["posts_1d_pct", "posts_7d_pct", "trend_ratio"]
62
41
  };
63
42
 
64
43
  return {
65
44
  "type": "object",
66
- "description": "Tracks the daily post count and 30-day trend for each asset ticker.",
67
- "patternProperties": {
68
- "^.*$": tickerSchema // Ticker
69
- },
45
+ "description": "Calculates the 1-day vs 7-day percentage of total posts for each asset.",
46
+ "patternProperties": { "^.*$": tickerSchema },
70
47
  "additionalProperties": tickerSchema
71
48
  };
72
49
  }
73
50
 
74
- // --- REFACTORED ---
75
- /**
76
- * @param {string} dateStr - Today's date.
77
- * @param {object} rootData - The root data object.
78
- * @param {object} dependencies - db, logger, calculationUtils
79
- */
80
- async process(dateStr, rootData, dependencies) {
81
- const { calculationUtils } = dependencies;
82
-
83
- // 1. Load this metric's history from yesterday
84
- // The runner must provide `loadYesterdayData` on calculationUtils
85
- const yHistoryData = await calculationUtils.loadYesterdayData('social-asset-posts-trend');
86
-
87
- // 2. Initialize state with history
88
- if (yHistoryData) {
89
- for (const [ticker, data] of Object.entries(yHistoryData)) {
90
- this.assetData.set(ticker, {
91
- count: 0, // Today's count starts at 0
92
- history: data.history_30d || []
93
- });
94
- }
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;
95
62
  }
96
-
97
- // 3. Process today's raw social data
98
- const todaySocialPosts = rootData.todaySocialPostInsights || {};
99
-
100
- for (const post of Object.values(todaySocialPosts)) {
101
- const tickers = post.tickers || [];
102
- for (const ticker of tickers) {
103
- if (!this.assetData.has(ticker)) {
104
- this.assetData.set(ticker, { count: 0, history: [] });
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++;
105
91
  }
106
- const data = this.assetData.get(ticker);
107
- data.count++;
108
92
  }
109
93
  }
110
- }
111
94
 
112
- async getResult() {
113
95
  const result = {};
114
- for (const [ticker, data] of this.assetData.entries()) {
115
- const history = data.history;
116
- const todayCount = data.count;
117
-
118
- const newHistory = [todayCount, ...history].slice(0, 30);
119
-
120
- let trend_30d_pct = 0;
121
- if (history.length > 0) {
122
- const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
123
- if (avg_30d > 0) {
124
- trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
125
- }
126
- }
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;
127
104
 
128
105
  result[ticker] = {
129
- count: todayCount,
130
- trend_30d_pct: trend_30d_pct,
131
- history_30d: newHistory
106
+ posts_1d_pct: pct1d,
107
+ posts_7d_pct: pct7d,
108
+ trend_ratio: (pct7d > 0) ? (pct1d / pct7d) : null
132
109
  };
133
110
  }
134
- return result;
111
+
112
+ this.result = result;
113
+ }
114
+ // --- END FIX ---
115
+
116
+ async getResult(fetchedDependencies) {
117
+ return this.result;
135
118
  }
136
119
 
137
120
  reset() {
138
- this.assetData.clear();
121
+ this.result = {};
139
122
  }
140
123
  }
141
124
 
@@ -1,143 +1,124 @@
1
1
  /**
2
- * @fileoverview Correlates social post volume spikes with topics from Gemini analysis.
3
- * This calculation ONLY uses the social post insights context.
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.
4
9
  */
5
-
6
10
  class SocialEventCorrelation {
11
+
7
12
  constructor() {
8
- this.todayTickerVolume = {};
9
- this.yesterdayTickerVolume = {};
10
- this.todayTopicCounts = {}; // { "TICKER": { "topic": count } }
11
- this.processed = false;
13
+ this.result = {};
12
14
  }
13
15
 
14
- // --- NEW ---
15
- /**
16
- * Statically defines all metadata for the manifest builder.
17
- */
18
16
  static getMetadata() {
19
17
  return {
20
- type: 'standard', // Runs per-user, but only executes logic once
18
+ type: 'meta',
21
19
  rootDataDependencies: ['social'],
22
- isHistorical: true, // Needs yesterday's social posts
23
- userType: 'all', // Runs on the first user and then stops
20
+ isHistorical: false,
21
+ userType: 'n/a',
24
22
  category: 'core_social'
25
23
  };
26
24
  }
27
25
 
28
- // --- NEW ---
29
- /**
30
- * Statically declare dependencies.
31
- */
32
26
  static getDependencies() {
33
27
  return [];
34
28
  }
35
29
 
36
- /**
37
- * Helper to process a set of posts and populate volume/topic counts.
38
- * @param {object} socialPostInsights - Map of { [postId]: postData }
39
- * @param {object} volumeTarget - The object to store volume counts.
40
- * @param {object} [topicTarget] - Optional. The object to store topic counts.
41
- */
42
- _processPosts(socialPostInsights, volumeTarget, topicTarget = null) {
43
- if (!socialPostInsights) return;
44
- const posts = Object.values(socialPostInsights);
45
-
46
- for (const post of posts) {
47
- if (!post.tickers || !Array.isArray(post.tickers)) continue;
48
-
49
- for (const ticker of post.tickers) {
50
- // 1. Aggregate Volume
51
- volumeTarget[ticker] = (volumeTarget[ticker] || 0) + 1;
52
-
53
- // 2. Aggregate Topics (if target provided)
54
- if (topicTarget) {
55
- if (!topicTarget[ticker]) {
56
- topicTarget[ticker] = {};
57
- }
58
- // New structure: post.sentiment is { overallSentiment: "...", topics: [...] }
59
- const topics = post.sentiment?.topics;
60
- if (topics && Array.isArray(topics)) {
61
- for (const topic of topics) {
62
- // Normalize topic to lowercase for consistent counting
63
- const normalizedTopic = topic.toLowerCase();
64
- topicTarget[ticker][normalizedTopic] = (topicTarget[ticker][normalizedTopic] || 0) + 1;
65
- }
66
- }
67
- }
30
+ 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
+ return {
41
+ "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
68
47
  }
48
+ };
49
+ }
50
+
51
+ _initEvent(result, eventName) {
52
+ if (!result[eventName]) {
53
+ result[eventName] = {
54
+ topic_distribution: {},
55
+ sentiment_distribution: {},
56
+ post_count: 0
57
+ };
69
58
  }
70
59
  }
71
60
 
72
- /**
73
- * @param {null} todayPortfolio - Not used.
74
- * @param {null} yesterdayPortfolio - Not used.
75
- * @param {null} userId - Not used.
76
- * @param {object} context - Shared context (e.g., mappings).
77
- * @param {object} todayInsights - (Not used)
78
- * @param {object} yesterdayInsights - (Not used)
79
- * @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
80
- * @param {object} yesterdaySocialPostInsights - Map of { [postId]: postData } for yesterday.
81
- */
82
- async process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights) {
83
-
84
- if (this.processed) {
85
- return; // Run only once
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;
86
71
  }
87
- this.processed = true;
88
72
 
89
- // 1. Process yesterday's posts for baseline volume
90
- this._processPosts(yesterdaySocialPostInsights, this.yesterdayTickerVolume);
73
+ for (const post of Object.values(socialDoc)) {
91
74
 
92
- // 2. Process today's posts for volume AND topic correlation
93
- this._processPosts(todaySocialPostInsights, this.todayTickerVolume, this.todayTopicCounts);
94
- }
75
+ // Infer events from schema.md fields
76
+ const topics = post.sentiment?.topics || [];
77
+ const sentiment = post.sentiment?.overallSentiment || 'Neutral';
78
+
79
+ const detectedEvents = new Set();
95
80
 
96
- async getResult() {
97
- if (!this.processed) return {};
98
-
99
- const results = {};
100
- const allTickers = new Set([
101
- ...Object.keys(this.todayTickerVolume),
102
- ...Object.keys(this.yesterdayTickerVolume)
103
- ]);
104
-
105
- for (const ticker of allTickers) {
106
- const todayVolume = this.todayTickerVolume[ticker] || 0;
107
- // Use 1 as baseline if yesterday was 0 to avoid -100% change, but 0 for spike calc
108
- const yesterdayVolumeForCalc = this.yesterdayTickerVolume[ticker] || 0;
109
-
110
- let volumeSpikePercent = 0;
111
- if (yesterdayVolumeForCalc > 0) {
112
- volumeSpikePercent = ((todayVolume - yesterdayVolumeForCalc) / yesterdayVolumeForCalc) * 100;
113
- } else if (todayVolume > 0) {
114
- volumeSpikePercent = todayVolume * 100.0; // e.g., 0 to 5 posts is a 500% increase
81
+ // Event 1: Check for Polls
82
+ if (post.pollData) {
83
+ detectedEvents.add('poll');
115
84
  }
116
85
 
117
- // Only report tickers with activity today
118
- if (todayVolume === 0) continue;
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
+ // }
119
94
 
120
- // Correlate topics
121
- const topics = this.todayTopicCounts[ticker] || {};
122
- const correlatedEvents = Object.entries(topics)
123
- .map(([topic, postCount]) => ({ topic, postCount }))
124
- .sort((a, b) => b.postCount - a.postCount); // Sort by count descending
95
+ if (detectedEvents.size === 0) continue;
125
96
 
126
- results[ticker] = {
127
- volume: todayVolume,
128
- volumeSpikePercent: volumeSpikePercent,
129
- correlatedEvents: correlatedEvents
130
- };
97
+ for (const eventName of detectedEvents) {
98
+ this._initEvent(result, eventName);
99
+ const eventData = result[eventName];
100
+ eventData.post_count++;
101
+
102
+ // Aggregate topics
103
+ for (const topic of topics) {
104
+ eventData.topic_distribution[topic] = (eventData.topic_distribution[topic] || 0) + 1;
105
+ }
106
+
107
+ // Aggregate sentiment
108
+ eventData.sentiment_distribution[sentiment] = (eventData.sentiment_distribution[sentiment] || 0) + 1;
109
+ }
131
110
  }
132
111
 
133
- return results;
112
+ this.result = result;
113
+ }
114
+ // --- END FIX ---
115
+
116
+ async getResult(fetchedDependencies) {
117
+ return this.result;
134
118
  }
135
119
 
136
120
  reset() {
137
- this.todayTickerVolume = {};
138
- this.yesterdayTickerVolume = {};
139
- this.todayTopicCounts = {};
140
- this.processed = false;
121
+ this.result = {};
141
122
  }
142
123
  }
143
124