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,145 +1,131 @@
1
1
  /**
2
- * @fileoverview Social Calculation (Pass 1)
3
- *
4
- * This metric answers: "How many times was each
5
- * predefined keyword mentioned in posts today?"
6
- *
7
- * This is a stateful calculation.
2
+ * @fileoverview Calculation (Pass 2 - Meta) for social word 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' (Array) to 'socialDoc' (Object).
7
+ * 4. Changed 'post.timestamp' to 'post.createdAt'.
8
+ * 5. Changed 'post.content' to 'post.textSnippet'.
8
9
  */
10
+ const STOPWORDS = new Set([
11
+ 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'being', 'been',
12
+ 'it', 'its', 'it\'s', 'i', 'me', 'my', 'mine', 'you', 'your', 'yours',
13
+ 'he', 'him', 'his', 'she', 'her', 'hers', 'they', 'them', 'their', 'theirs',
14
+ 'we', 'us', 'our', 'ours', 'what', 'which', 'who', 'whom', 'this', 'that',
15
+ 'these', 'those', 'am', 'and', 'but', 'if', 'or', 'because', 'as', 'until',
16
+ 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between',
17
+ 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to',
18
+ 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again',
19
+ 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how',
20
+ 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some',
21
+ 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too',
22
+ 'very', 's', 't', 'can', 'will', 'just', 'don', 'should', 'now',
23
+ 'stock', 'stocks', 'market', 'price', 'trade', 'trading', 'buy', 'sell',
24
+ 'long', 'short', 'chart', 'week', 'day', 'today', 'going', 'think'
25
+ ]);
26
+
9
27
  class SocialWordMentionsTrend {
28
+
10
29
  constructor() {
11
- // { [keyword]: { count: 0, history: [] } }
12
- this.keywordData = new Map();
13
- // Keywords are loaded from config
14
- this.keywords = [];
30
+ this.result = {};
15
31
  }
16
32
 
17
- // --- NEW ---
18
- /**
19
- * Statically defines all metadata for the manifest builder.
20
- */
21
33
  static getMetadata() {
22
34
  return {
23
35
  type: 'meta',
24
36
  rootDataDependencies: ['social'],
25
- isHistorical: false, // Stateful
26
- userType: 'n/a',
37
+ isHistorical: true, // Needs 7 days of posts
38
+ userType: 'n'/'a',
27
39
  category: 'core_social'
28
40
  };
29
41
  }
30
42
 
31
- // --- NEW ---
32
- /**
33
- * Statically declare dependencies.
34
- */
35
43
  static getDependencies() {
36
- return [];
44
+ return ['social-top-mentioned-words'];
37
45
  }
38
46
 
39
- /**
40
- * Defines the output schema for this calculation.
41
- * @returns {object} JSON Schema object
42
- */
43
47
  static getSchema() {
44
- const keywordSchema = {
48
+ const wordSchema = {
45
49
  "type": "object",
46
- "description": "Daily and trended post count for a specific keyword.",
47
50
  "properties": {
48
- "count": {
49
- "type": "number",
50
- "description": "Today's raw mention count."
51
- },
52
- "trend_30d_pct": {
53
- "type": "number",
54
- "description": "Percentage change from the 30-day average count."
55
- },
56
- "history_30d": {
57
- "type": "array",
58
- "description": "30-day history of mention counts (today first).",
59
- "items": { "type": "number" }
60
- }
61
- },
62
- "required": ["count", "trend_30d_pct", "history_30d"]
51
+ "count_1d": { "type": "number" },
52
+ "count_7d": { "type": "number" }
53
+ }
63
54
  };
64
55
 
65
56
  return {
66
57
  "type": "object",
67
- "description": "Tracks the daily mention count and 30-day trend for predefined keywords.",
68
- "patternProperties": {
69
- "^.*$": keywordSchema // Keyword
70
- },
71
- "additionalProperties": keywordSchema
58
+ "description": "Tracks the 1-day vs 7-day mention count for the top 50 words.",
59
+ "patternProperties": { "^.*$": wordSchema },
60
+ "additionalProperties": wordSchema
72
61
  };
73
62
  }
74
63
 
75
- // --- REFACTORED ---
76
- /**
77
- * @param {string} dateStr - Today's date.
78
- * @param {object} rootData - The root data object.
79
- * @param {object} dependencies - db, logger, calculationUtils
80
- * @param {object} config - Computation config.
81
- */
82
- async process(dateStr, rootData, dependencies, config) {
83
- const { calculationUtils } = dependencies;
84
-
85
- // 1. Load keywords from config (or another source)
86
- this.keywords = config.trackedKeywords || ['inflation', 'fed', 'AI', 'crypto', 'recession'];
87
-
88
- // 2. Load this metric's history from yesterday
89
- const yHistoryData = await calculationUtils.loadYesterdayData('social-word-mentions-trend');
90
-
91
- // 3. Initialize state for all keywords
92
- for (const keyword of this.keywords) {
93
- const normalizedKey = keyword.toLowerCase();
94
- this.keywordData.set(normalizedKey, {
95
- count: 0,
96
- history: yHistoryData?.[normalizedKey]?.history_30d || []
97
- });
64
+ _tokenize(text) {
65
+ if (!text) return [];
66
+ return text.toLowerCase()
67
+ .replace(/[^\w\s$]/g, '')
68
+ .split(/\s+/)
69
+ .filter(word =>
70
+ word.length > 2 &&
71
+ !STOPWORDS.has(word) &&
72
+ !/^\d+$/.test(word)
73
+ );
74
+ }
75
+
76
+ // --- THIS IS THE FIX ---
77
+ async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
78
+ // dateStr is Arg 1 (metaPayload), rootData is Arg 2, dependencies is Arg 3
79
+
80
+ // Get social data from Arg 1 (worker hack)
81
+ const socialDoc = dateStr.social; // This IS the map of posts
82
+ // Get dependency from Arg 5
83
+ const topWordsData = fetchedDependencies['social-top-mentioned-words'];
84
+
85
+ if (!socialDoc || typeof socialDoc !== 'object' || !topWordsData) {
86
+ this.result = {};
87
+ return;
98
88
  }
99
-
100
- // 4. Process today's raw social data
101
- const todaySocialPosts = rootData.todaySocialPostInsights || {};
102
-
103
- for (const post of Object.values(todaySocialPosts)) {
104
- const text = (post.fullText || '').toLowerCase();
105
-
106
- for (const keyword of this.keywords) {
107
- const normalizedKey = keyword.toLowerCase();
108
- if (text.includes(normalizedKey)) {
109
- this.keywordData.get(normalizedKey).count++;
110
- }
111
- }
89
+
90
+ const topWords = new Set(Object.keys(topWordsData));
91
+ // dateStr is the metaPayload, but it has a 'date' field
92
+ const today = new Date(dateStr.date + 'T00:00:00Z');
93
+ const sevenDaysAgo = new Date(today);
94
+ sevenDaysAgo.setUTCDate(today.getUTCDate() - 7);
95
+
96
+ const counts = new Map();
97
+ for (const word of topWords) {
98
+ counts.set(word, { count_1d: 0, count_7d: 0 });
112
99
  }
113
- }
114
100
 
115
- async getResult() {
116
- const result = {};
117
- for (const [keyword, data] of this.keywordData.entries()) {
118
- const history = data.history;
119
- const todayCount = data.count;
120
-
121
- const newHistory = [todayCount, ...history].slice(0, 30);
122
-
123
- let trend_30d_pct = 0;
124
- if (history.length > 0) {
125
- const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
126
- if (avg_30d > 0) {
127
- trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
101
+ for (const post of Object.values(socialDoc)) {
102
+ const postDate = new Date(post.createdAt); // Use 'createdAt'
103
+ const is1d = postDate.toISOString().slice(0, 10) === dateStr.date;
104
+ const is7d = postDate >= sevenDaysAgo;
105
+
106
+ if (!is7d) continue;
107
+
108
+ const words = this._tokenize(post.textSnippet); // Use 'textSnippet'
109
+
110
+ for (const word of words) {
111
+ if (counts.has(word)) {
112
+ const stats = counts.get(word);
113
+ if (is7d) stats.count_7d++;
114
+ if (is1d) stats.count_1d++;
128
115
  }
129
116
  }
130
-
131
- result[keyword] = {
132
- count: todayCount,
133
- trend_30d_pct: trend_30d_pct,
134
- history_30d: newHistory
135
- };
136
117
  }
137
- return result;
118
+
119
+ this.result = Object.fromEntries(counts);
120
+ }
121
+ // --- END FIX ---
122
+
123
+ async getResult(fetchedDependencies) {
124
+ return this.result;
138
125
  }
139
126
 
140
127
  reset() {
141
- this.keywordData.clear();
142
- this.keywords = [];
128
+ this.result = {};
143
129
  }
144
130
  }
145
131
 
@@ -1,53 +1,17 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
2
+ * @fileoverview Calculation (Pass 1) for speculator sentiment.
3
3
  *
4
- * This metric answers: "For each asset, what are the average
5
- * P&L, leverage, SL rate, and TP rate for *long* and *short*
6
- * speculator positions?"
4
+ * This metric answers: "For each asset, what is the sentiment
5
+ * (long vs. short) *only* for speculators?"
7
6
  */
8
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
7
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
9
8
 
10
9
  class SpeculatorAssetSentiment {
11
10
  constructor() {
12
- // { [instrumentId]: { long: {...}, short: {...} } }
11
+ // { [instrumentId]: { long: 0, short: 0 } }
13
12
  this.assets = new Map();
14
- this.mappings = null;
15
- }
16
-
17
- /**
18
- * Defines the output schema for this calculation.
19
- * @returns {object} JSON Schema object
20
- */
21
- static getSchema() {
22
- const sentimentDetailSchema = {
23
- "type": "object",
24
- "properties": {
25
- "avg_pnl_pct": { "type": "number" },
26
- "avg_leverage": { "type": "number" },
27
- "sl_rate_pct": { "type": "number" },
28
- "tp_rate_pct": { "type": "number" },
29
- "count": { "type": "number" }
30
- },
31
- "required": ["avg_pnl_pct", "avg_leverage", "sl_rate_pct", "tp_rate_pct", "count"]
32
- };
33
-
34
- const tickerSchema = {
35
- "type": "object",
36
- "properties": {
37
- "long": sentimentDetailSchema,
38
- "short": sentimentDetailSchema
39
- },
40
- "required": ["long", "short"]
41
- };
42
-
43
- return {
44
- "type": "object",
45
- "description": "Aggregates P&L, leverage, and SL/TP usage for speculators, split by long/short positions per asset.",
46
- "patternProperties": {
47
- "^.*$": tickerSchema // Ticker
48
- },
49
- "additionalProperties": tickerSchema
50
- };
13
+ // --- STANDARD 0: RENAMED ---
14
+ this.tickerMap = null;
51
15
  }
52
16
 
53
17
  /**
@@ -58,8 +22,8 @@ class SpeculatorAssetSentiment {
58
22
  type: 'standard',
59
23
  rootDataDependencies: ['portfolio'],
60
24
  isHistorical: false,
61
- userType: 'speculator',
62
- category: 'core_speculator'
25
+ userType: 'speculator', // <-- KEY: Only runs for speculators
26
+ category: 'core_sentiment'
63
27
  };
64
28
  }
65
29
 
@@ -70,28 +34,43 @@ class SpeculatorAssetSentiment {
70
34
  return [];
71
35
  }
72
36
 
37
+ /**
38
+ * Defines the output schema for this calculation.
39
+ */
40
+ static getSchema() {
41
+ const tickerSchema = {
42
+ "type": "object",
43
+ "properties": {
44
+ "long_count": { "type": "number" },
45
+ "short_count": { "type": "number" },
46
+ "sentiment_ratio": { "type": ["number", "null"] }
47
+ },
48
+ "required": ["long_count", "short_count", "sentiment_ratio"]
49
+ };
50
+
51
+ return {
52
+ "type": "object",
53
+ "description": "Calculates the long/short sentiment ratio *only* for speculators.",
54
+ "patternProperties": { "^.*$": tickerSchema },
55
+ "additionalProperties": tickerSchema
56
+ };
57
+ }
58
+
73
59
  _initAsset(instrumentId) {
74
60
  if (!this.assets.has(instrumentId)) {
75
- const createSide = () => ({
76
- pnl_sum: 0,
77
- leverage_sum: 0,
78
- sl_set_count: 0,
79
- tp_set_count: 0,
80
- count: 0
81
- });
82
- this.assets.set(instrumentId, {
83
- long: createSide(),
84
- short: createSide()
85
- });
61
+ this.assets.set(instrumentId, { long: 0, short: 0 });
86
62
  }
87
63
  }
88
64
 
89
- process(portfolioData) {
90
- if (portfolioData?.context?.userType !== 'speculator') {
91
- return;
65
+ // --- STANDARD 0: UPDATED SIGNATURE ---
66
+ process(todayPortfolio, yesterdayPortfolio, userId, context) {
67
+ // --- STANDARD 0: ADDED ---
68
+ if (!this.tickerMap) {
69
+ this.tickerMap = context.instrumentToTicker;
92
70
  }
93
-
94
- const positions = portfolioData.PublicPositions;
71
+
72
+ // Only speculators are processed, so we can assume PublicPositions exists
73
+ const positions = todayPortfolio.PublicPositions;
95
74
  if (!positions || !Array.isArray(positions)) {
96
75
  return;
97
76
  }
@@ -101,52 +80,44 @@ class SpeculatorAssetSentiment {
101
80
  if (!instrumentId) continue;
102
81
 
103
82
  this._initAsset(instrumentId);
104
- const sideData = pos.IsBuy ? this.assets.get(instrumentId).long : this.assets.get(instrumentId).short;
105
-
106
- const pnl_percent = (pos.NetProfit || 0) / (pos.Amount || 1);
107
-
108
- sideData.pnl_sum += pnl_percent;
109
- sideData.leverage_sum += pos.Leverage || 1;
110
- if (pos.StopLossRate) sideData.sl_set_count++;
111
- if (pos.TakeProfitRate) sideData.tp_set_count++;
112
- sideData.count++;
113
- }
114
- }
115
-
116
- _calculateAverages(data) {
117
- const count = data.count;
118
- if (count === 0) {
119
- return { avg_pnl_pct: 0, avg_leverage: 0, sl_rate_pct: 0, tp_rate_pct: 0, count: 0 };
83
+ const assetData = this.assets.get(instrumentId);
84
+
85
+ if (pos.IsBuy) {
86
+ assetData.long++;
87
+ } else {
88
+ assetData.short++;
89
+ }
120
90
  }
121
- return {
122
- avg_pnl_pct: data.pnl_sum / count,
123
- avg_leverage: data.leverage_sum / count,
124
- sl_rate_pct: (data.sl_set_count / count) * 100,
125
- tp_rate_pct: (data.tp_set_count / count) * 100,
126
- count: count
127
- };
128
91
  }
129
92
 
130
93
  async getResult() {
131
- if (!this.mappings) {
132
- this.mappings = await loadInstrumentMappings();
94
+ // --- STANDARD 0: REMOVED forbidden data load ---
95
+
96
+ // Failsafe check
97
+ if (!this.tickerMap) {
98
+ return {}; // process() must run first
133
99
  }
134
100
 
135
101
  const result = {};
136
102
  for (const [instrumentId, data] of this.assets.entries()) {
137
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
103
+ // --- STANDARD 0: SIMPLIFIED ---
104
+ const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
138
105
 
139
- result[ticker] = {
140
- long: this._calculateAverages(data.long),
141
- short: this._calculateAverages(data.short)
142
- };
106
+ if (data.long > 0 || data.short > 0) {
107
+ result[ticker] = {
108
+ long_count: data.long,
109
+ short_count: data.short,
110
+ sentiment_ratio: (data.short > 0) ? (data.long / data.short) : null
111
+ };
112
+ }
143
113
  }
144
114
  return result;
145
115
  }
146
116
 
147
117
  reset() {
148
118
  this.assets.clear();
149
- this.mappings = null;
119
+ // --- STANDARD 0: RENAMED ---
120
+ this.tickerMap = null;
150
121
  }
151
122
  }
152
123