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,20 +1,13 @@
1
1
  /**
2
2
  * @fileoverview GEM Product Line (Pass 2)
3
- * --- FIX ---
4
- * - Added defensive checks in '_loadDependencies' to prevent crash
5
- * when dependencies fail to load (e.g., due to worker bug).
6
- * - **FIXED:** Restored logic to calculate 'avg_position_change_pct'
7
- * to resolve the hardcoded '0' value. This involves
8
- * re-adding fields to '_initFlowData' and 'process'.
3
+ * REFACTORED: Uses context.computed and context.math.extract.
9
4
  */
10
-
11
5
  class SkilledCohortFlow {
12
6
  constructor() {
13
7
  this.assetFlows = new Map();
14
8
  this.cohortMap = new Map();
15
- this.tickerMap = null;
16
9
  this.dependenciesLoaded = false;
17
- this.priceChangeMap = null;
10
+ this.tickerMap = null;
18
11
  }
19
12
 
20
13
  static getMetadata() {
@@ -28,10 +21,7 @@ class SkilledCohortFlow {
28
21
  }
29
22
 
30
23
  static getDependencies() {
31
- return [
32
- 'cohort-skill-definition', // from gem (Pass 1)
33
- 'instrument-price-change-1d' // from core (Pass 1)
34
- ];
24
+ return ['cohort-skill-definition', 'instrument-price-change-1d'];
35
25
  }
36
26
 
37
27
  static getSchema() {
@@ -45,208 +35,110 @@ class SkilledCohortFlow {
45
35
  },
46
36
  "required": ["net_flow_pct", "net_flow_contribution", "avg_position_change_pct", "user_count"]
47
37
  };
48
-
49
- return {
50
- "type": "object",
51
- "description": "Calculates capital flow and conviction change for the 'Skilled' cohort.",
52
- "patternProperties": { "^.*$": tickerSchema },
53
- "additionalProperties": tickerSchema
54
- };
55
- }
56
-
57
- _getPortfolioPositions(portfolio) {
58
- // --- FIX: Support both Normal (Aggregated) and Speculator (Public) ---
59
- return portfolio?.AggregatedPositions || portfolio?.PublicPositions;
38
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
60
39
  }
61
40
 
62
- // --- THIS IS THE FIX (Part 1) ---
63
41
  _initFlowData(instrumentId) {
64
42
  if (!this.assetFlows.has(instrumentId)) {
65
43
  this.assetFlows.set(instrumentId, {
66
- total_invested_yesterday: 0,
67
- total_invested_today: 0,
68
- price_change_yesterday: 0,
69
- // Restore fields needed for avg_position_change_pct
70
- total_pos_size_yesterday: 0,
71
- total_pos_size_today: 0,
72
- user_count_yesterday: 0,
73
- user_count_today: 0
44
+ total_invested_yesterday: 0, total_invested_today: 0, price_change_yesterday: 0,
45
+ total_pos_size_yesterday: 0, total_pos_size_today: 0, user_count_yesterday: 0, user_count_today: 0
74
46
  });
75
47
  }
76
48
  }
77
- // --- END FIX (Part 1) ---
78
49
 
79
- _loadDependencies(fetchedDependencies) {
50
+ _loadDependencies(computed) {
80
51
  if (this.dependenciesLoaded) return;
81
-
82
- if (!fetchedDependencies) {
83
- // This will stop the test and explain the root cause.
84
- throw new Error(
85
- `[skilled-cohort-flow] CRITICAL ERROR: fetchedDependencies object was UNDEFINED.
86
- This means a dependency test (like 'cohort-skill-definition' or 'instrument-price-change-1d')
87
- failed to run or produced an invalid result.`
88
- );
89
- }
90
-
91
- const cohortData = fetchedDependencies['cohort-skill-definition'];
92
- if (!cohortData) {
93
- throw new Error(
94
- `[skilled-cohort-flow] DEPENDENCY ERROR: 'cohort-skill-definition' was missing.
95
- Check logs for errors in that calculation.`
96
- );
97
- }
98
-
99
- // This logic is correct for 'cohort-skill-definition' output
100
- if (cohortData && cohortData.skilled_user_ids) {
52
+ const cohortData = computed['cohort-skill-definition'];
53
+ if (cohortData) {
101
54
  (cohortData.skilled_user_ids || []).forEach(uid => this.cohortMap.set(String(uid), 'skilled'));
102
55
  }
103
-
104
- this.priceChangeMap = fetchedDependencies['instrument-price-change-1d'];
105
- if (!this.priceChangeMap) {
106
- throw new Error(
107
- `[skilled-cohort-flow] DEPENDENCY ERROR: 'instrument-price-change-1d' was missing.
108
- This is likely due to the worker bug for 'price' dependencies.`
109
- );
110
- }
111
-
112
56
  this.dependenciesLoaded = true;
113
57
  }
114
58
 
115
- // --- THIS IS THE FIX (Part 2) ---
116
- process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
117
- // Load dependencies *before* checking cohort
118
- this._loadDependencies(fetchedDependencies);
119
-
120
- if (!this.tickerMap) {
121
- this.tickerMap = context.instrumentToTicker;
122
- }
123
-
124
- const cohortName = this.cohortMap.get(String(userId)); // Ensure string ID
125
- if (cohortName !== 'skilled') {
126
- return; // Not in skilled cohort
127
- }
59
+ process(context) {
60
+ const { user, computed, mappings, math } = context;
61
+ const { extract } = math;
62
+
63
+ this._loadDependencies(computed);
64
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
128
65
 
129
- if (!todayPortfolio || !yesterdayPortfolio || !this.priceChangeMap || !this.tickerMap) {
130
- return;
131
- }
66
+ if (this.cohortMap.get(user.id) !== 'skilled') return;
67
+
68
+ const priceChangeMap = computed['instrument-price-change-1d'];
69
+ if (!priceChangeMap) return;
132
70
 
133
- const yPos = this._getPortfolioPositions(yesterdayPortfolio);
134
- const tPos = this._getPortfolioPositions(todayPortfolio);
135
- if (!yPos || !tPos) return;
71
+ const yPos = extract.getPositions(user.portfolio.yesterday, user.type);
72
+ const tPos = extract.getPositions(user.portfolio.today, user.type);
136
73
 
137
- const yPosMap = new Map(yPos.map(p => [p.InstrumentID, p]));
138
- const tPosMap = new Map(tPos.map(p => [p.InstrumentID, p]));
74
+ const yPosMap = new Map(yPos.map(p => [extract.getInstrumentId(p), p]));
75
+ const tPosMap = new Map(tPos.map(p => [extract.getInstrumentId(p), p]));
139
76
  const allInstrumentIds = new Set([...yPosMap.keys(), ...tPosMap.keys()]);
140
77
 
141
- for (const instrumentId of allInstrumentIds) {
142
- if (!instrumentId) continue;
143
-
144
- this._initFlowData(instrumentId);
145
- const asset = this.assetFlows.get(instrumentId);
146
-
147
- const yP = yPosMap.get(instrumentId);
148
- const tP = tPosMap.get(instrumentId);
149
-
150
- // Get $ flow
151
- const yInvested = yP?.Invested || yP?.Amount || 0;
152
- const tInvested = tP?.Invested || tP?.Amount || 0;
153
-
154
- // Get position size (same as $ flow for these schemas)
155
- const ySize = yP?.Invested || yP?.Amount || 0;
156
- const tSize = tP?.Invested || tP?.Amount || 0;
157
-
158
- if (yInvested > 0) {
159
- asset.total_invested_yesterday += yInvested;
78
+ for (const instId of allInstrumentIds) {
79
+ if (!instId) continue;
80
+ this._initFlowData(instId);
81
+ const asset = this.assetFlows.get(instId);
160
82
 
161
- const ticker = this.tickerMap[instrumentId];
162
- const yPriceChange_pct = (ticker && this.priceChangeMap[ticker])
163
- ? this.priceChangeMap[ticker].price_change_1d_pct
164
- : 0;
83
+ const yWeight = extract.getPositionWeight(yPosMap.get(instId), user.type);
84
+ const tWeight = extract.getPositionWeight(tPosMap.get(instId), user.type);
165
85
 
166
- const yPriceChange_decimal = (yPriceChange_pct || 0) / 100.0;
167
- asset.price_change_yesterday += yPriceChange_decimal * yInvested;
168
-
169
- // Track yesterday's size and user count
170
- asset.total_pos_size_yesterday += ySize;
86
+ if (yWeight > 0) {
87
+ asset.total_invested_yesterday += yWeight;
88
+ const ticker = mappings.instrumentToTicker[instId];
89
+ const priceChange = (ticker && priceChangeMap[ticker]) ? priceChangeMap[ticker].change_1d_pct : 0;
90
+ asset.price_change_yesterday += (priceChange / 100.0) * yWeight;
91
+ asset.total_pos_size_yesterday += yWeight;
171
92
  asset.user_count_yesterday++;
172
93
  }
173
- if (tInvested > 0) {
174
- asset.total_invested_today += tInvested;
175
- asset.user_count_today++; // Count users holding today
176
-
177
- // Track today's size
178
- asset.total_pos_size_today += tSize;
94
+ if (tWeight > 0) {
95
+ asset.total_invested_today += tWeight;
96
+ asset.user_count_today++;
97
+ asset.total_pos_size_today += tWeight;
179
98
  }
180
99
  }
181
100
  }
182
- // --- END FIX (Part 2) ---
183
101
 
184
- // --- THIS IS THE FIX (Part 3) ---
185
102
  async getResult() {
186
- if (!this.tickerMap) {
187
- return {};
188
- }
189
-
103
+ if (!this.tickerMap) return {};
190
104
  const finalResult = {};
191
-
192
- for (const [instrumentId, data] of this.assetFlows.entries()) {
193
- const ticker = this.tickerMap[instrumentId];
105
+ for (const [instId, data] of this.assetFlows.entries()) {
106
+ const ticker = this.tickerMap[instId];
194
107
  if (!ticker) continue;
195
-
196
- const {
197
- total_invested_yesterday, total_invested_today, price_change_yesterday,
198
- // Destructure the restored fields
199
- total_pos_size_yesterday, total_pos_size_today,
200
- user_count_yesterday, user_count_today
201
- } = data;
202
-
203
- let net_flow_percentage = 0;
204
- let flow_contribution = 0;
205
-
206
- if (total_invested_yesterday > 0) {
207
- const avg_price_change_decimal = (price_change_yesterday === 0) ? 0 : (price_change_yesterday / total_invested_yesterday);
208
- const price_adjusted_yesterday_value = total_invested_yesterday * (1 + avg_price_change_decimal);
209
-
210
- flow_contribution = total_invested_today - price_adjusted_yesterday_value;
211
- net_flow_percentage = (flow_contribution / total_invested_yesterday) * 100;
212
- } else if (total_invested_today > 0) {
213
- flow_contribution = total_invested_today;
214
- net_flow_percentage = Infinity;
215
- }
216
-
217
- // Restore the calculation logic for conviction
218
- let avg_position_change_pct = 0;
219
- if (user_count_yesterday > 0 && user_count_today > 0) {
220
- const avg_pos_y = total_pos_size_yesterday / user_count_yesterday;
221
- const avg_pos_t = total_pos_size_today / user_count_today;
222
- if (avg_pos_y > 0) {
223
- avg_position_change_pct = ((avg_pos_t - avg_pos_y) / avg_pos_y) * 100;
224
- }
225
- } else if (user_count_today > 0) {
226
- // Pure inflow, conviction is max
227
- avg_position_change_pct = Infinity;
228
- }
229
-
230
- if (isFinite(net_flow_percentage) && isFinite(flow_contribution)) {
231
- finalResult[ticker] = {
232
- net_flow_pct: net_flow_percentage,
233
- net_flow_contribution: flow_contribution,
234
- avg_position_change_pct: avg_position_change_pct, // Now correctly calculated
235
- user_count: user_count_today
236
- };
108
+ // ... (Same calculation logic as before, just boilerplate math)
109
+ let net_flow = 0, flow_contrib = 0;
110
+ if (data.total_invested_yesterday > 0) {
111
+ const avg_change = data.price_change_yesterday / data.total_invested_yesterday;
112
+ const adjusted_y = data.total_invested_yesterday * (1 + avg_change);
113
+ flow_contrib = data.total_invested_today - adjusted_y;
114
+ net_flow = (flow_contrib / data.total_invested_yesterday) * 100;
115
+ } else if (data.total_invested_today > 0) {
116
+ flow_contrib = data.total_invested_today;
117
+ net_flow = Infinity;
237
118
  }
119
+
120
+ let avg_pos_change = 0;
121
+ if (data.user_count_yesterday > 0 && data.user_count_today > 0) {
122
+ const avg_y = data.total_pos_size_yesterday / data.user_count_yesterday;
123
+ const avg_t = data.total_pos_size_today / data.user_count_today;
124
+ if (avg_y > 0) avg_pos_change = ((avg_t - avg_y) / avg_y) * 100;
125
+ } else if (data.user_count_today > 0) avg_pos_change = Infinity;
126
+
127
+ finalResult[ticker] = {
128
+ net_flow_pct: isFinite(net_flow) ? net_flow : 0,
129
+ net_flow_contribution: isFinite(flow_contrib) ? flow_contrib : 0,
130
+ avg_position_change_pct: isFinite(avg_pos_change) ? avg_pos_change : 0,
131
+ user_count: data.user_count_today
132
+ };
238
133
  }
239
-
240
134
  return finalResult;
241
135
  }
242
- // --- END FIX (Part 3) ---
243
136
 
244
137
  reset() {
245
138
  this.assetFlows.clear();
246
139
  this.cohortMap.clear();
247
- this.tickerMap = null;
248
140
  this.dependenciesLoaded = false;
249
- this.priceChangeMap = null;
141
+ this.tickerMap = null;
250
142
  }
251
143
  }
252
144
  module.exports = SkilledCohortFlow;
@@ -1,17 +1,10 @@
1
1
  /**
2
2
  * @fileoverview GEM Product Line (Pass 3)
3
- *
4
- * This 'meta' calculation answers: "What is the net flow
5
- * divergence between Skilled and Unskilled cohorts?"
3
+ * REFACTORED: Uses context.math.signals and process(context).
6
4
  */
7
5
  class SkilledUnskilledDivergence {
6
+ constructor() { this.result = {}; }
8
7
 
9
- // --- STANDARD 2: ADDED ---
10
- constructor() {
11
- this.result = {};
12
- }
13
-
14
- /** Statically defines metadata */
15
8
  static getMetadata() {
16
9
  return {
17
10
  type: 'meta',
@@ -22,17 +15,8 @@ class SkilledUnskilledDivergence {
22
15
  };
23
16
  }
24
17
 
25
- /** Statically declare dependencies */
26
- static getDependencies() {
27
- return [
28
- 'skilled-cohort-flow', // from gem (Pass 2)
29
- 'unskilled-cohort-flow' // from gem (Pass 2)
30
- ];
31
- }
32
-
33
- /**
34
- * Defines the output schema for this calculation.
35
- */
18
+ static getDependencies() { return ['skilled-cohort-flow', 'unskilled-cohort-flow']; }
19
+
36
20
  static getSchema() {
37
21
  const tickerSchema = {
38
22
  "type": "object",
@@ -43,71 +27,32 @@ class SkilledUnskilledDivergence {
43
27
  },
44
28
  "required": ["skilled_flow_pct", "unskilled_flow_pct", "flow_divergence_score"]
45
29
  };
46
-
47
- return {
48
- "type": "object",
49
- "description": "Tracks the net % flow divergence between skilled and unskilled cohorts.",
50
- "patternProperties": { "^.*$": tickerSchema },
51
- "additionalProperties": tickerSchema
52
- };
30
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
53
31
  }
54
32
 
55
- /**
56
- * This is a 'meta' calculation. It runs once.
57
- */
58
- // --- STANDARD 1: UPDATED SIGNATURE (added rootData) ---
59
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
60
- const { logger } = dependencies;
61
-
62
- const skilledFlow = fetchedDependencies['skilled-cohort-flow'];
63
- const unskilledFlow = fetchedDependencies['unskilled-cohort-flow'];
64
-
65
- if (!skilledFlow || !unskilledFlow) {
66
- logger.log('WARN', `[gem/skilled-unskilled-divergence] Missing dependencies for ${dateStr}.`);
67
- // --- STANDARD 2: DO NOT RETURN ---
68
- this.result = {};
69
- return;
70
- }
71
-
72
- const allTickers = new Set([
73
- ...Object.keys(skilledFlow),
74
- ...Object.keys(unskilledFlow)
75
- ]);
33
+ process(context) {
34
+ const { computed, math } = context;
35
+ const { signals } = math;
76
36
 
37
+ const tickers = signals.getUnionKeys(computed, ['skilled-cohort-flow', 'unskilled-cohort-flow']);
77
38
  const result = {};
78
39
 
79
- for (const ticker of allTickers) {
80
- const skilledData = skilledFlow[ticker];
81
- const unskilledData = unskilledFlow[ticker];
82
-
83
- // Get net_flow_pct
84
- const skilled_flow_pct = skilledData?.net_flow_pct || 0;
85
- const unskilled_flow_pct = unskilledData?.net_flow_pct || 0;
40
+ for (const ticker of tickers) {
41
+ const skilled = signals.getMetric(computed, 'skilled-cohort-flow', ticker, 'net_flow_pct');
42
+ const unskilled = signals.getMetric(computed, 'unskilled-cohort-flow', ticker, 'net_flow_pct');
86
43
 
87
- // Only include assets that are actively being traded
88
- if (skilled_flow_pct === 0 && unskilled_flow_pct === 0) {
89
- continue;
90
- }
44
+ if (skilled === 0 && unskilled === 0) continue;
91
45
 
92
46
  result[ticker] = {
93
- skilled_flow_pct: skilled_flow_pct,
94
- unskilled_flow_pct: unskilled_flow_pct,
95
- flow_divergence_score: skilled_flow_pct - unskilled_flow_pct
47
+ skilled_flow_pct: skilled,
48
+ unskilled_flow_pct: unskilled,
49
+ flow_divergence_score: signals.divergence(skilled, unskilled)
96
50
  };
97
51
  }
98
-
99
- // --- STANDARD 2: SET STATE, DO NOT RETURN ---
100
52
  this.result = result;
101
53
  }
102
54
 
103
- // --- STANDARD 2: ADDED ---
104
- async getResult(fetchedDependencies) {
105
- return this.result;
106
- }
107
-
108
- // --- STANDARD 2: ADDED ---
109
- reset() {
110
- this.result = {};
111
- }
55
+ async getResult() { return this.result; }
56
+ reset() { this.result = {}; }
112
57
  }
113
58
  module.exports = SkilledUnskilledDivergence;