aiden-shared-calculations-unified 1.0.150 → 1.0.152

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.
@@ -15,8 +15,7 @@ class BehavioralAnomaly {
15
15
  type: 'standard',
16
16
  isHistorical: true,
17
17
 
18
- // RESTORED: Request full 60-day history for both Portfolio and Rankings.
19
- // CachedDataLoader handles this efficiently via reference pointers.
18
+ // Request 60 days of history for baseline
20
19
  rootDataSeries: {
21
20
  portfolio: 60,
22
21
  rankings: 60
@@ -57,7 +56,6 @@ class BehavioralAnomaly {
57
56
 
58
57
  let sumSquares = 0;
59
58
  let totalValue = 0;
60
-
61
59
  positions.forEach(p => totalValue += DataExtractor.getPositionValue(p));
62
60
 
63
61
  if (totalValue === 0) return 0;
@@ -73,7 +71,6 @@ class BehavioralAnomaly {
73
71
  calculateMartingaleScore(tradeHistory) {
74
72
  if (!tradeHistory || tradeHistory.length < 2) return 0;
75
73
 
76
- // Trades are already filtered by date in the main loop logic
77
74
  const sorted = [...tradeHistory].sort((a, b) => new Date(a.CloseDateTime) - new Date(b.CloseDateTime));
78
75
  const recentTrades = sorted.slice(-30);
79
76
 
@@ -98,7 +95,6 @@ class BehavioralAnomaly {
98
95
 
99
96
  getDailyVector(portfolio, rankings, history, math) {
100
97
  const { RankingsExtractor } = math;
101
-
102
98
  const hhi = this.calculateHHI(portfolio, math);
103
99
  const mScore = this.calculateMartingaleScore(history);
104
100
 
@@ -115,7 +111,7 @@ class BehavioralAnomaly {
115
111
  return [hhi, mScore, strain, risk];
116
112
  }
117
113
 
118
- process(context) {
114
+ async process(context) {
119
115
  const { math, globalData } = context;
120
116
  const { LinearAlgebra } = math;
121
117
  const userId = context.user.id;
@@ -123,8 +119,6 @@ class BehavioralAnomaly {
123
119
  const seriesData = globalData?.series?.root || {};
124
120
  const portfolioSeries = seriesData.portfolio || {};
125
121
  const rankingsSeries = seriesData.rankings || {};
126
-
127
- // Full Trade History (Contains ALL time, so we must filter it)
128
122
  const fullHistory = context.user.history || [];
129
123
 
130
124
  const availableDates = Object.keys(portfolioSeries).sort();
@@ -135,56 +129,37 @@ class BehavioralAnomaly {
135
129
  const datePortfolio = portfolioSeries[date][userId];
136
130
  if (!datePortfolio) continue;
137
131
 
138
- // CRITICAL FIX: Filter History to avoid "Time Travel"
139
- // We only look at trades that had closed ON or BEFORE this specific loop date.
140
132
  const loopDateObj = new Date(date);
141
133
  const historyAsOfDate = fullHistory.filter(t => new Date(t.CloseDateTime) <= loopDateObj);
142
-
143
- // Robust Lookup: Find ranking for this date (or closest recent date)
144
134
  const rankingsSnapshot = this.findClosestData(date, rankingsSeries);
145
135
  const userRanking = rankingsSnapshot ? rankingsSnapshot.find(r => String(r.CustomerId) === String(userId)) : null;
146
136
 
147
- const vec = this.getDailyVector(
148
- datePortfolio,
149
- userRanking,
150
- historyAsOfDate, // Pass filtered history
151
- math
152
- );
137
+ const vec = this.getDailyVector(datePortfolio, userRanking, historyAsOfDate, math);
153
138
  trainingVectors.push(vec);
154
139
  }
155
140
 
156
141
  // 2. Minimum Viable Baseline
142
+ const localResults = {}; // Local container to satisfy Guard logic
157
143
  const MIN_DATAPOINTS = 15;
144
+
158
145
  if (trainingVectors.length < MIN_DATAPOINTS) {
159
- this.results[userId] = {
160
- status: 'INSUFFICIENT_BASELINE',
161
- dataPoints: trainingVectors.length
162
- };
163
- return;
146
+ localResults[userId] = { status: 'INSUFFICIENT_BASELINE', dataPoints: trainingVectors.length };
147
+ this.results = localResults;
148
+ return this.results;
164
149
  }
165
150
 
166
- // 3. Compute Statistics (Covariance Matrix)
151
+ // 3. Compute Statistics
167
152
  const stats = LinearAlgebra.covarianceMatrix(trainingVectors);
168
153
  const inverseCov = LinearAlgebra.invertMatrix(stats.matrix);
169
154
 
170
- // Singular Matrix Check (If behavior is perfectly identical every day)
171
155
  if (!inverseCov) {
172
- this.results[userId] = { status: 'STABLE_STATE', info: 'Variance too low' };
173
- return;
156
+ localResults[userId] = { status: 'STABLE_STATE', info: 'Variance too low' };
157
+ this.results = localResults;
158
+ return this.results;
174
159
  }
175
160
 
176
161
  // 4. Compute Today's Vector
177
- // We use the explicit 'today' injection, or fallback to the series
178
- const todayRanking = context.user.rankEntry ||
179
- (this.findClosestData(context.date.today, rankingsSeries) || [])
180
- .find(r => String(r.CustomerId) === String(userId));
181
-
182
- const todayVector = this.getDailyVector(
183
- context.user.portfolio.today,
184
- todayRanking,
185
- fullHistory, // Today includes all history
186
- math
187
- );
162
+ const todayVector = this.getDailyVector(context.user.portfolio.today, context.user.rankEntry, fullHistory, math);
188
163
 
189
164
  // 5. Calculate Distance
190
165
  const distance = LinearAlgebra.mahalanobisDistance(todayVector, stats.means, inverseCov);
@@ -204,27 +179,30 @@ class BehavioralAnomaly {
204
179
  }
205
180
  });
206
181
 
207
- this.results[userId] = {
182
+ localResults[userId] = {
208
183
  triggered: true,
209
184
  anomalyScore: parseFloat(distance.toFixed(2)),
210
185
  primaryDriver: primaryDriver,
211
186
  driverSignificance: `${maxZ.toFixed(1)}σ`,
212
187
  baselineDays: trainingVectors.length,
213
- vectors: {
214
- current: todayVector,
215
- baselineMeans: stats.means
216
- }
188
+ vectors: { current: todayVector, baselineMeans: stats.means }
217
189
  };
218
190
 
219
191
  // Add CID to index for downstream alert handlers
220
- if (!this.results.cids) this.results.cids = [];
221
- this.results.cids.push(userId);
192
+ if (!localResults.cids) localResults.cids = [];
193
+ localResults.cids.push(userId);
222
194
  } else {
223
- this.results[userId] = {
224
- triggered: false,
225
- anomalyScore: parseFloat(distance.toFixed(2))
226
- };
195
+ localResults[userId] = { triggered: false, anomalyScore: parseFloat(distance.toFixed(2)) };
227
196
  }
197
+
198
+ // --- FINAL ASSIGNMENT ---
199
+ // Satisfies the Guard AND hands data to getResult()
200
+ this.results = localResults;
201
+ return this.results;
202
+ }
203
+
204
+ async getResult() {
205
+ return this.results;
228
206
  }
229
207
  }
230
208
 
@@ -6,10 +6,8 @@ class AumLeaderboard {
6
6
  return {
7
7
  type: 'meta',
8
8
  category: 'analytics',
9
- // We only need the rankings root data
10
9
  rootDataDependencies: ['rankings'],
11
10
  mandatoryRoots: ['rankings'],
12
- // Daily schedule is standard for leaderboards
13
11
  schedule: { type: 'DAILY' }
14
12
  };
15
13
  }
@@ -42,7 +40,6 @@ class AumLeaderboard {
42
40
  return {
43
41
  username: entry.UserName || entry.username || "Unknown",
44
42
  cid: String(entry.CustomerId || entry.cid),
45
- // Use Extractor for safety
46
43
  aum: RankingsExtractor.getAUMValue(entry)
47
44
  };
48
45
  });
@@ -59,9 +56,12 @@ class AumLeaderboard {
59
56
  // 4. Output Global Result
60
57
  this.results = rankedResult;
61
58
 
62
- // CRITICAL FIX: MetaExecutor requires the data to be returned
59
+ // Critical: Return for MetaExecutor wrapping
63
60
  return this.results;
64
61
  }
62
+
63
+ // CRITICAL FIX: Interface required by ResultCommitter
64
+ async getResult() { return this.results; }
65
65
  }
66
66
 
67
67
  module.exports = AumLeaderboard;
@@ -46,6 +46,7 @@ class GlobalAumPerAsset30D {
46
46
  // CRITICAL FIX: MetaExecutor requires the data to be returned
47
47
  return this.results;
48
48
  }
49
+ async getResult() { return this.results; }
49
50
  }
50
51
 
51
52
  module.exports = GlobalAumPerAsset30D;
@@ -96,6 +96,11 @@ class PIDailyAssetAUM {
96
96
  averageMap: rollingAverage
97
97
  }
98
98
  };
99
+ return this.results;
100
+ }
101
+
102
+ async getResult() {
103
+ return this.results;
99
104
  }
100
105
  }
101
106
 
@@ -89,6 +89,7 @@ class PiAnomalyDetector {
89
89
  }
90
90
 
91
91
  this.results[userId] = alerts.length > 0 ? alerts : null;
92
+ return this.results;
92
93
  }
93
94
 
94
95
  async getResult() {
@@ -2,9 +2,10 @@
2
2
  * @fileoverview Asset Recommendation Engine.
3
3
  * Suggests assets based on Similar Users and Sector Gaps.
4
4
  */
5
-
6
5
  class PiAssetRecommender {
7
- constructor() { this.results = {}; }
6
+ constructor() {
7
+ this.results = {};
8
+ }
8
9
 
9
10
  static getMetadata() {
10
11
  return {
@@ -15,6 +16,8 @@ class PiAssetRecommender {
15
16
  }
16
17
 
17
18
  static getDependencies() {
19
+ // Expects these to be available in context.computed
20
+ // (Ensure these are Meta computations or small enough to be passed in memory)
18
21
  return ['PiSectorAnalysis', 'PiSimilarityMatrix', 'PiSimilarityVector'];
19
22
  }
20
23
 
@@ -25,15 +28,16 @@ class PiAssetRecommender {
25
28
  };
26
29
  }
27
30
 
28
- process(context) {
31
+ async process(context) {
29
32
  // 1. Access computed results from previous steps
30
33
  const similarityMatrix = context.computed['PiSimilarityMatrix'];
31
34
  const vectors = context.computed['PiSimilarityVector'];
32
- const { mappings } = context;
35
+ const mappings = context.mappings || {};
33
36
 
34
37
  // Defensive Check: If inputs missing, return empty object
35
38
  if (!similarityMatrix || !vectors) {
36
- return {};
39
+ this.results = {};
40
+ return this.results;
37
41
  }
38
42
 
39
43
  const recommendations = {};
@@ -75,10 +79,12 @@ class PiAssetRecommender {
75
79
  recommendations[userId] = sorted;
76
80
  }
77
81
 
78
- // [FIX] CLEANER APPROACH:
79
- // Instead of assigning to a confusing 'meta' key, we simply RETURN the data.
80
- // The MetaExecutor will automatically save this under the correct 'global' key.
81
- return recommendations;
82
+ // --- CRITICAL FIX ---
83
+ // 1. Save to Class Property (Required by ResultCommitter via getResult)
84
+ this.results = recommendations;
85
+
86
+ // 2. Return to MetaExecutor (Required for wrapping)
87
+ return this.results;
82
88
  }
83
89
 
84
90
  async getResult() {
@@ -47,6 +47,7 @@ class PiAumOverTime {
47
47
  }
48
48
 
49
49
  this.results[userId] = Number(aum.toFixed(2));
50
+ return this.results;
50
51
  }
51
52
 
52
53
  async getResult() {
@@ -79,6 +79,7 @@ class PiCryptoBuyers {
79
79
  isCryptoBuyer,
80
80
  lastCryptoTrade
81
81
  };
82
+ return this.results;
82
83
  }
83
84
 
84
85
  async getResult() {
@@ -2,7 +2,6 @@
2
2
  * @fileoverview Similarity Matrix Meta-Computation.
3
3
  * Calculates cosine similarity between all PI vectors to find nearest neighbors.
4
4
  */
5
-
6
5
  class PiSimilarityMatrix {
7
6
  constructor() { this.results = {}; }
8
7
 
@@ -24,12 +23,14 @@ class PiSimilarityMatrix {
24
23
  };
25
24
  }
26
25
 
27
- process(context) {
28
- const vectorMap = context.computed['PiSimilarityVector'];
29
- // [FIX] Ensure we have enough peers to actually compare
30
- if (!vectorMap || Object.keys(vectorMap).length < 2) {
31
- return {}; // Return TRUE empty so ResultCommitter skips the write
32
- }
26
+ async process(context) {
27
+ const vectorMap = context.computed['PiSimilarityVector'];
28
+
29
+ // [FIX] Ensure we have enough peers to actually compare
30
+ if (!vectorMap || Object.keys(vectorMap).length < 2) {
31
+ this.results = {}; // Explicitly set empty
32
+ return this.results;
33
+ }
33
34
 
34
35
  const userIds = Object.keys(vectorMap);
35
36
  const matrix = {};
@@ -87,8 +88,12 @@ class PiSimilarityMatrix {
87
88
  matrix[u1] = matches.sort((a,b) => b.score - a.score).slice(0, 5);
88
89
  }
89
90
 
90
- // [FIX] MetaExecutor requires return value
91
- return matrix;
91
+ // --- CRITICAL FIX ---
92
+ // 1. Assign to class property (for getResult)
93
+ this.results = matrix;
94
+
95
+ // 2. Return to MetaExecutor (for wrapping)
96
+ return this.results;
92
97
  }
93
98
 
94
99
  async getResult() {
@@ -4,7 +4,9 @@
4
4
  */
5
5
 
6
6
  class PiTotalAum {
7
- constructor() { this.results = {}; }
7
+ constructor() {
8
+ this.results = {}; // Container for getResult()
9
+ }
8
10
 
9
11
  static getMetadata() {
10
12
  return {
@@ -14,32 +16,48 @@ class PiTotalAum {
14
16
  };
15
17
  }
16
18
 
17
- static getDependencies() { return ['PiAumOverTime']; }
19
+ static getDependencies() {
20
+ return ['PiAumOverTime'];
21
+ }
18
22
 
19
23
  static getSchema() {
20
- return { type: 'number', description: 'Total AUM of all tracked PIs' };
24
+ return {
25
+ type: 'number',
26
+ description: 'Total AUM of all tracked PIs'
27
+ };
21
28
  }
22
29
 
23
- process(context) {
30
+ async process(context) {
31
+ // 1. Get the map of CID -> AUM from the dependency
24
32
  const aumMap = context.computed['PiAumOverTime'];
25
33
 
26
- // [FIX] Return 0 if data missing
27
34
  if (!aumMap) {
28
- return 0;
35
+ this.results = 0;
36
+ return this.results;
29
37
  }
30
38
 
31
39
  let total = 0;
32
40
  for (const val of Object.values(aumMap)) {
33
- // Validate it's a number
34
- if (typeof val === 'number') total += val;
41
+ // Ensure we are adding numbers
42
+ if (typeof val === 'number') {
43
+ total += val;
44
+ }
35
45
  }
36
46
 
47
+ // 2. Format result
37
48
  const result = Number(total.toFixed(2));
38
49
 
39
- // [FIX] MetaExecutor requires return value
40
- return result;
50
+ // --- CRITICAL FIX ---
51
+ // A. Set the property for the Committer (ResultCommitter.js line 72)
52
+ this.results = result;
53
+
54
+ // B. Return for the Executor (MetaExecutor.js line 176)
55
+ return this.results;
41
56
  }
42
57
 
58
+ /**
59
+ * REQUIRED: Interface for the storage layer.
60
+ */
43
61
  async getResult() {
44
62
  return this.results;
45
63
  }
@@ -166,7 +166,11 @@ class RecommendedPopularInvestors {
166
166
  this.results = {
167
167
  [context.user.id]: recommendations.slice(0, 5)
168
168
  };
169
+ return this.results;
169
170
  }
171
+ // CRITICAL FIX: Interface required by ResultCommitter
172
+ async getResult() { return this.results; }
173
+
170
174
  }
171
175
 
172
176
  module.exports = RecommendedPopularInvestors;
@@ -55,9 +55,11 @@ class RiskLeaderboard {
55
55
 
56
56
  this.results = rankedResult;
57
57
 
58
- // CRITICAL FIX: MetaExecutor requires the data to be returned
59
58
  return this.results;
60
59
  }
60
+
61
+ // CRITICAL FIX: Interface required by ResultCommitter
62
+ async getResult() { return this.results; }
61
63
  }
62
64
 
63
65
  module.exports = RiskLeaderboard;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiden-shared-calculations-unified",
3
- "version": "1.0.150",
3
+ "version": "1.0.152",
4
4
  "description": "Shared calculation modules for the BullTrackers Computation System.",
5
5
  "main": "index.js",
6
6
  "files": [