bulltrackers-module 1.0.766 → 1.0.769

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 (76) hide show
  1. package/functions/computation-system-v2/UserPortfolioMetrics.js +50 -0
  2. package/functions/computation-system-v2/computations/BehavioralAnomaly.js +559 -227
  3. package/functions/computation-system-v2/computations/GlobalAumPerAsset30D.js +103 -0
  4. package/functions/computation-system-v2/computations/NewSectorExposure.js +82 -35
  5. package/functions/computation-system-v2/computations/NewSocialPost.js +52 -24
  6. package/functions/computation-system-v2/computations/PIDailyAssetAUM.js +134 -0
  7. package/functions/computation-system-v2/computations/PiFeatureVectors.js +227 -0
  8. package/functions/computation-system-v2/computations/PiRecommender.js +359 -0
  9. package/functions/computation-system-v2/computations/PopularInvestorProfileMetrics.js +354 -641
  10. package/functions/computation-system-v2/computations/SignedInUserList.js +51 -0
  11. package/functions/computation-system-v2/computations/SignedInUserMirrorHistory.js +138 -0
  12. package/functions/computation-system-v2/computations/SignedInUserPIProfileMetrics.js +106 -0
  13. package/functions/computation-system-v2/computations/SignedInUserProfileMetrics.js +324 -0
  14. package/functions/computation-system-v2/config/bulltrackers.config.js +40 -126
  15. package/functions/computation-system-v2/core-api.js +17 -9
  16. package/functions/computation-system-v2/data_schema_reference.MD +108 -0
  17. package/functions/computation-system-v2/devtools/builder/builder.js +362 -0
  18. package/functions/computation-system-v2/devtools/builder/examples/user-metrics.yaml +26 -0
  19. package/functions/computation-system-v2/devtools/index.js +36 -0
  20. package/functions/computation-system-v2/devtools/shared/MockDataFactory.js +235 -0
  21. package/functions/computation-system-v2/devtools/shared/SchemaTemplates.js +475 -0
  22. package/functions/computation-system-v2/devtools/shared/SystemIntrospector.js +517 -0
  23. package/functions/computation-system-v2/devtools/shared/index.js +16 -0
  24. package/functions/computation-system-v2/devtools/simulation/DAGAnalyzer.js +243 -0
  25. package/functions/computation-system-v2/devtools/simulation/MockDataFetcher.js +306 -0
  26. package/functions/computation-system-v2/devtools/simulation/MockStorageManager.js +336 -0
  27. package/functions/computation-system-v2/devtools/simulation/SimulationEngine.js +525 -0
  28. package/functions/computation-system-v2/devtools/simulation/SimulationServer.js +581 -0
  29. package/functions/computation-system-v2/devtools/simulation/index.js +17 -0
  30. package/functions/computation-system-v2/devtools/simulation/simulate.js +324 -0
  31. package/functions/computation-system-v2/devtools/vscode-computation/package.json +90 -0
  32. package/functions/computation-system-v2/devtools/vscode-computation/snippets/computation.json +128 -0
  33. package/functions/computation-system-v2/devtools/vscode-computation/src/extension.ts +401 -0
  34. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/codeActions.ts +152 -0
  35. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/completions.ts +207 -0
  36. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/diagnostics.ts +205 -0
  37. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/hover.ts +205 -0
  38. package/functions/computation-system-v2/devtools/vscode-computation/tsconfig.json +22 -0
  39. package/functions/computation-system-v2/docs/HowToCreateComputations.MD +602 -0
  40. package/functions/computation-system-v2/framework/core/Manifest.js +9 -16
  41. package/functions/computation-system-v2/framework/core/RunAnalyzer.js +2 -1
  42. package/functions/computation-system-v2/framework/data/DataFetcher.js +330 -126
  43. package/functions/computation-system-v2/framework/data/MaterializedViewManager.js +84 -0
  44. package/functions/computation-system-v2/framework/data/QueryBuilder.js +38 -38
  45. package/functions/computation-system-v2/framework/execution/Orchestrator.js +226 -153
  46. package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +17 -19
  47. package/functions/computation-system-v2/framework/storage/StateRepository.js +32 -2
  48. package/functions/computation-system-v2/framework/storage/StorageManager.js +111 -83
  49. package/functions/computation-system-v2/framework/testing/ComputationTester.js +161 -66
  50. package/functions/computation-system-v2/handlers/dispatcher.js +57 -29
  51. package/functions/computation-system-v2/legacy/PiAssetRecommender.js.old +115 -0
  52. package/functions/computation-system-v2/legacy/PiSimilarityMatrix.js +104 -0
  53. package/functions/computation-system-v2/legacy/PiSimilarityVector.js +71 -0
  54. package/functions/computation-system-v2/scripts/debug_aggregation.js +25 -0
  55. package/functions/computation-system-v2/scripts/test-computation-dag.js +109 -0
  56. package/functions/computation-system-v2/scripts/test-invalidation-scenarios.js +234 -0
  57. package/functions/task-engine/helpers/data_storage_helpers.js +6 -6
  58. package/package.json +1 -1
  59. package/functions/computation-system-v2/computations/PopularInvestorRiskAssessment.js +0 -176
  60. package/functions/computation-system-v2/computations/PopularInvestorRiskMetrics.js +0 -294
  61. package/functions/computation-system-v2/computations/UserPortfolioSummary.js +0 -172
  62. package/functions/computation-system-v2/scripts/migrate-sectors.js +0 -73
  63. package/functions/computation-system-v2/test/analyze-results.js +0 -238
  64. package/functions/computation-system-v2/test/other/test-dependency-cascade.js +0 -150
  65. package/functions/computation-system-v2/test/other/test-dispatcher.js +0 -317
  66. package/functions/computation-system-v2/test/other/test-framework.js +0 -500
  67. package/functions/computation-system-v2/test/other/test-real-execution.js +0 -166
  68. package/functions/computation-system-v2/test/other/test-real-integration.js +0 -194
  69. package/functions/computation-system-v2/test/other/test-refactor-e2e.js +0 -131
  70. package/functions/computation-system-v2/test/other/test-results.json +0 -31
  71. package/functions/computation-system-v2/test/other/test-risk-metrics-computation.js +0 -329
  72. package/functions/computation-system-v2/test/other/test-scheduler.js +0 -204
  73. package/functions/computation-system-v2/test/other/test-storage.js +0 -449
  74. package/functions/computation-system-v2/test/run-pipeline-test.js +0 -554
  75. package/functions/computation-system-v2/test/test-full-pipeline.js +0 -227
  76. package/functions/computation-system-v2/test/test-worker-pool.js +0 -266
@@ -0,0 +1,243 @@
1
+ /**
2
+ * @fileoverview DAG Analyzer
3
+ *
4
+ * Analyzes the computation DAG for visualization and debugging.
5
+ * Provides pass levels, dependency chains, and execution order info.
6
+ */
7
+
8
+ class DAGAnalyzer {
9
+ /**
10
+ * @param {Object} introspector - SystemIntrospector instance
11
+ */
12
+ constructor(introspector) {
13
+ this.introspector = introspector;
14
+ }
15
+
16
+ /**
17
+ * Get full DAG structure for visualization.
18
+ * @returns {Object} { nodes: [], edges: [] }
19
+ */
20
+ getVisualizationData() {
21
+ const graph = this.introspector.getDependencyGraph();
22
+ const levels = this.introspector.getPassLevels();
23
+ const configs = this.introspector.getAllComputationConfigs();
24
+
25
+ const nodes = [];
26
+ const edges = [];
27
+
28
+ for (const [name, deps] of graph) {
29
+ const config = configs.get(name);
30
+ nodes.push({
31
+ id: name,
32
+ label: name,
33
+ level: levels.get(name) || 1,
34
+ type: config?.type || 'unknown',
35
+ category: config?.category || 'unknown',
36
+ isHistorical: config?.isHistorical || false
37
+ });
38
+
39
+ for (const dep of deps) {
40
+ edges.push({
41
+ source: dep,
42
+ target: name,
43
+ id: `${dep}->${name}`
44
+ });
45
+ }
46
+ }
47
+
48
+ return { nodes, edges };
49
+ }
50
+
51
+ /**
52
+ * Get execution plan for a target computation.
53
+ * @param {string} targetComputation
54
+ * @returns {Object} Execution plan
55
+ */
56
+ getExecutionPlan(targetComputation) {
57
+ const graph = this.introspector.getDependencyGraph();
58
+ const levels = this.introspector.getPassLevels();
59
+
60
+ const order = [];
61
+ const visited = new Set();
62
+
63
+ const visit = (name) => {
64
+ if (visited.has(name)) return;
65
+ visited.add(name);
66
+
67
+ const deps = graph.get(name) || [];
68
+ for (const dep of deps) {
69
+ visit(dep);
70
+ }
71
+
72
+ order.push(name);
73
+ };
74
+
75
+ visit(targetComputation);
76
+
77
+ // Group by pass level
78
+ const byPass = {};
79
+ for (const name of order) {
80
+ const level = levels.get(name) || 1;
81
+ if (!byPass[level]) byPass[level] = [];
82
+ byPass[level].push(name);
83
+ }
84
+
85
+ return {
86
+ target: targetComputation,
87
+ totalComputations: order.length,
88
+ executionOrder: order,
89
+ byPassLevel: byPass,
90
+ estimatedPasses: Object.keys(byPass).length
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Find all paths from source to target computation.
96
+ * @param {string} source
97
+ * @param {string} target
98
+ * @returns {string[][]} Array of paths
99
+ */
100
+ findPaths(source, target) {
101
+ const graph = this.introspector.getDependencyGraph();
102
+ const paths = [];
103
+
104
+ const dfs = (current, path) => {
105
+ if (current === target) {
106
+ paths.push([...path, current]);
107
+ return;
108
+ }
109
+
110
+ const deps = graph.get(current) || [];
111
+ for (const dep of deps) {
112
+ if (!path.includes(dep)) {
113
+ dfs(dep, [...path, current]);
114
+ }
115
+ }
116
+ };
117
+
118
+ dfs(source, []);
119
+ return paths;
120
+ }
121
+
122
+ /**
123
+ * Get impact analysis - what would be affected if a computation changes.
124
+ * @param {string} computationName
125
+ * @returns {Object}
126
+ */
127
+ getImpactAnalysis(computationName) {
128
+ const graph = this.introspector.getDependencyGraph();
129
+ const affected = new Set();
130
+
131
+ // Find all computations that depend (directly or indirectly) on this one
132
+ const findDependents = (name) => {
133
+ for (const [candidate, deps] of graph) {
134
+ if (deps.includes(name) && !affected.has(candidate)) {
135
+ affected.add(candidate);
136
+ findDependents(candidate);
137
+ }
138
+ }
139
+ };
140
+
141
+ findDependents(computationName);
142
+
143
+ return {
144
+ computation: computationName,
145
+ directDependents: [...graph.entries()]
146
+ .filter(([_, deps]) => deps.includes(computationName))
147
+ .map(([name]) => name),
148
+ allAffected: Array.from(affected),
149
+ impactScore: affected.size
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Detect cycles in the DAG (should always be empty for valid DAG).
155
+ * @returns {string[][]} Any cycles found
156
+ */
157
+ detectCycles() {
158
+ const graph = this.introspector.getDependencyGraph();
159
+ const cycles = [];
160
+ const visited = new Set();
161
+ const recursionStack = new Set();
162
+
163
+ const dfs = (node, path) => {
164
+ if (recursionStack.has(node)) {
165
+ const cycleStart = path.indexOf(node);
166
+ cycles.push(path.slice(cycleStart));
167
+ return;
168
+ }
169
+ if (visited.has(node)) return;
170
+
171
+ visited.add(node);
172
+ recursionStack.add(node);
173
+
174
+ const deps = graph.get(node) || [];
175
+ for (const dep of deps) {
176
+ dfs(dep, [...path, node]);
177
+ }
178
+
179
+ recursionStack.delete(node);
180
+ };
181
+
182
+ for (const node of graph.keys()) {
183
+ dfs(node, []);
184
+ }
185
+
186
+ return cycles;
187
+ }
188
+
189
+ /**
190
+ * Get summary statistics about the DAG.
191
+ * @returns {Object}
192
+ */
193
+ getSummary() {
194
+ const graph = this.introspector.getDependencyGraph();
195
+ const levels = this.introspector.getPassLevels();
196
+
197
+ let totalEdges = 0;
198
+ let maxInDegree = 0;
199
+ let maxOutDegree = 0;
200
+
201
+ const outDegree = new Map();
202
+ const inDegree = new Map();
203
+
204
+ for (const [name, deps] of graph) {
205
+ outDegree.set(name, deps.length);
206
+ totalEdges += deps.length;
207
+ maxOutDegree = Math.max(maxOutDegree, deps.length);
208
+
209
+ for (const dep of deps) {
210
+ inDegree.set(dep, (inDegree.get(dep) || 0) + 1);
211
+ }
212
+ }
213
+
214
+ for (const [_, count] of inDegree) {
215
+ maxInDegree = Math.max(maxInDegree, count);
216
+ }
217
+
218
+ // Find entry points (no dependencies)
219
+ const entryPoints = [...graph.entries()]
220
+ .filter(([_, deps]) => deps.length === 0)
221
+ .map(([name]) => name);
222
+
223
+ // Find exit points (no dependents)
224
+ const allDeps = new Set();
225
+ for (const deps of graph.values()) {
226
+ for (const dep of deps) allDeps.add(dep);
227
+ }
228
+ const exitPoints = [...graph.keys()].filter(name => !allDeps.has(name) || true);
229
+
230
+ return {
231
+ totalNodes: graph.size,
232
+ totalEdges,
233
+ totalPasses: this.introspector.getTotalPasses(),
234
+ maxInDegree,
235
+ maxOutDegree,
236
+ entryPointCount: entryPoints.length,
237
+ entryPoints: entryPoints.slice(0, 5), // First 5
238
+ cycles: this.detectCycles()
239
+ };
240
+ }
241
+ }
242
+
243
+ module.exports = { DAGAnalyzer };
@@ -0,0 +1,306 @@
1
+ /**
2
+ * @fileoverview Mock Data Fetcher
3
+ * * Drop-in replacement for DataFetcher that returns mock data.
4
+ * Designed to be injected into the real Orchestrator for 1:1 simulation.
5
+ */
6
+
7
+ const { MockDataFactory } = require('../shared/MockDataFactory');
8
+
9
+ class MockDataFetcher {
10
+ /**
11
+ * @param {Object} options
12
+ * @param {Object} [options.mockDataFactory] - Custom MockDataFactory instance
13
+ * @param {Object} [options.introspector] - SystemIntrospector for metadata
14
+ * @param {Object} [options.preloadedData] - Pre-generated data { tableName: { entityId: rows[] } }
15
+ * @param {Object} [options.computationResults] - Results from earlier passes
16
+ * @param {number} [options.entityCount=10] - Default entity count
17
+ * @param {string[]} [options.entityIds] - Specific entity IDs to generate
18
+ */
19
+ constructor(options = {}) {
20
+ this.mockDataFactory = options.mockDataFactory || new MockDataFactory(options);
21
+ this.introspector = options.introspector || null;
22
+ this.preloadedData = options.preloadedData || {};
23
+ this.computationResults = options.computationResults || {};
24
+ this.entityCount = options.entityCount || 10;
25
+ this.entityIds = options.entityIds || null;
26
+
27
+ // Stats tracking (mimics real DataFetcher)
28
+ this.stats = {
29
+ queriesExecuted: 0,
30
+ rowsReturned: 0,
31
+ tablesAccessed: new Set(),
32
+ errors: []
33
+ };
34
+
35
+ // Query log for inspection
36
+ this.queryLog = [];
37
+ }
38
+
39
+ /**
40
+ * Main entry point - matches DataFetcher.fetchForComputation signature.
41
+ * * @param {Object} requires - Computation requirements
42
+ * @param {Date|string} targetDate - Target date
43
+ * @param {string[]|null} entities - Specific entities to fetch
44
+ * @returns {Object} Data keyed by table name
45
+ */
46
+ async fetchForComputation(requires, targetDate, entities = null) {
47
+ const results = {};
48
+ const errors = [];
49
+
50
+ const effectiveEntities = entities || this.entityIds || null;
51
+
52
+ for (const [key, spec] of Object.entries(requires)) {
53
+ try {
54
+ // Handle metric type (computation dependency)
55
+ if (spec.type === 'metric') {
56
+ results[key] = await this._fetchMetric(spec, targetDate, effectiveEntities);
57
+ continue;
58
+ }
59
+
60
+ // Log the query
61
+ this.queryLog.push({
62
+ table: key,
63
+ targetDate,
64
+ lookback: spec.lookback || 0,
65
+ entities: effectiveEntities,
66
+ timestamp: new Date().toISOString()
67
+ });
68
+
69
+ // Fetch mock data
70
+ const data = await this.fetch({
71
+ table: key,
72
+ targetDate,
73
+ lookback: spec.lookback || 0,
74
+ mandatory: spec.mandatory || false,
75
+ fields: spec.fields || null,
76
+ entities: effectiveEntities
77
+ });
78
+
79
+ results[key] = data;
80
+ this.stats.tablesAccessed.add(key);
81
+
82
+ // Check mandatory
83
+ if (spec.mandatory && this._isEmpty(data)) {
84
+ errors.push({ table: key, reason: 'MANDATORY_MISSING' });
85
+ }
86
+
87
+ } catch (e) {
88
+ if (spec.mandatory) {
89
+ errors.push({ table: key, reason: e.message });
90
+ }
91
+ results[key] = null;
92
+ }
93
+ }
94
+
95
+ if (errors.length > 0) {
96
+ const msg = errors.map(e => `${e.table}: ${e.reason}`).join(', ');
97
+ this.stats.errors.push(msg);
98
+ throw new Error(`[MockDataFetcher] Missing mandatory data: ${msg}`);
99
+ }
100
+
101
+ return results;
102
+ }
103
+
104
+ /**
105
+ * Low-level fetch - matches DataFetcher.fetch signature.
106
+ * * @param {Object} options
107
+ * @returns {Object} Data keyed by entity ID
108
+ */
109
+ async fetch(options) {
110
+ // Destructure 'lookback' and 'targetDate' from options
111
+ const { table, targetDate, lookback = 0, entities = null, fields = null } = options;
112
+
113
+ this.stats.queriesExecuted++;
114
+
115
+ // Check for preloaded data first
116
+ if (this.preloadedData[table]) {
117
+ const preloaded = this.preloadedData[table];
118
+ if (entities) {
119
+ const filtered = {};
120
+ for (const id of entities) {
121
+ if (preloaded[id]) {
122
+ filtered[id] = preloaded[id];
123
+ this.stats.rowsReturned += preloaded[id].length;
124
+ }
125
+ }
126
+ return filtered;
127
+ }
128
+ this.stats.rowsReturned += Object.values(preloaded).reduce((sum, rows) => sum + rows.length, 0);
129
+ return preloaded;
130
+ }
131
+
132
+ // Generate mock data
133
+ try {
134
+ const entityIds = entities || this.entityIds;
135
+ const { byEntity } = this.mockDataFactory.generate(table, {
136
+ entityCount: this.entityCount,
137
+ entityIds,
138
+ // FIX: Use 'lookback' and 'targetDate' variables, not 'daysBack'/'asOfDate'
139
+ daysBack: lookback || 0,
140
+ asOfDate: targetDate
141
+ });
142
+
143
+ // Convert Map to Object
144
+ const result = {};
145
+ for (const [id, rows] of byEntity) {
146
+ result[id] = rows;
147
+ this.stats.rowsReturned += rows.length;
148
+ }
149
+
150
+ return result;
151
+ } catch (e) {
152
+ // Log error to see why generation failed (optional but helpful)
153
+ console.error(`[MockDataFetcher] Generation failed for ${table}:`, e.message);
154
+ // Table not in templates - return empty data
155
+ return {};
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Fetch metric (computation result from earlier pass).
161
+ */
162
+ async _fetchMetric(spec, targetDate, entities) {
163
+ const computationName = spec.computation;
164
+ if (!computationName) {
165
+ throw new Error('Metric spec missing "computation" field');
166
+ }
167
+
168
+ const cached = this.computationResults[computationName];
169
+ if (!cached) {
170
+ // Generate empty metric result
171
+ return {};
172
+ }
173
+
174
+ // Filter by entities if specified
175
+ if (entities) {
176
+ const filtered = {};
177
+ for (const id of entities) {
178
+ if (cached[id]) {
179
+ filtered[id] = cached[id];
180
+ }
181
+ }
182
+ return filtered;
183
+ }
184
+
185
+ return cached;
186
+ }
187
+
188
+ /**
189
+ * Batched fetch - for streaming computations.
190
+ * Returns an async iterator that yields { data, entityIds } batches.
191
+ * NOTE: This is NOT async because Orchestrator calls it without await.
192
+ */
193
+ fetchComputationBatched(requires, targetDate, batchSize = 1000) {
194
+ const self = this;
195
+
196
+ // Return an async iterable that yields batches in correct format
197
+ return {
198
+ [Symbol.asyncIterator]() {
199
+ let yielded = false;
200
+ return {
201
+ async next() {
202
+ if (yielded) {
203
+ return { done: true };
204
+ }
205
+ yielded = true;
206
+
207
+ // Fetch data inside the iterator
208
+ const allData = await self.fetchForComputation(requires, targetDate, self.entityIds);
209
+
210
+ // Get all entity IDs from the first table that has data
211
+ const firstTableWithData = Object.keys(allData).find(k => allData[k] && Object.keys(allData[k]).length > 0);
212
+ const entityIds = firstTableWithData ? Object.keys(allData[firstTableWithData]) : (self.entityIds || []);
213
+
214
+ return {
215
+ done: false,
216
+ value: { data: allData, entityIds }
217
+ };
218
+ }
219
+ };
220
+ }
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Check data availability.
226
+ * Returns { canRun: boolean, missing: string[], available: { table: { available, rowCount } } }
227
+ */
228
+ async checkAvailability(requires, targetDate) {
229
+ // In mock mode, all data is always available
230
+ const available = {};
231
+ for (const table of Object.keys(requires)) {
232
+ available[table] = { available: true, rowCount: this.entityCount };
233
+ }
234
+ return {
235
+ canRun: true,
236
+ missing: [],
237
+ available
238
+ };
239
+ }
240
+
241
+ /**
242
+ * Check if data exists for a table.
243
+ */
244
+ async hasData(table, targetDate) {
245
+ return true; // Mock always has data
246
+ }
247
+
248
+ /**
249
+ * Get fetch statistics.
250
+ */
251
+ getStats() {
252
+ return {
253
+ ...this.stats,
254
+ tablesAccessed: Array.from(this.stats.tablesAccessed)
255
+ };
256
+ }
257
+
258
+ /**
259
+ * Reset statistics.
260
+ */
261
+ resetStats() {
262
+ this.stats = {
263
+ queriesExecuted: 0,
264
+ rowsReturned: 0,
265
+ tablesAccessed: new Set(),
266
+ errors: []
267
+ };
268
+ this.queryLog = [];
269
+ }
270
+
271
+ /**
272
+ * Clear cache (no-op for mock).
273
+ */
274
+ clearCache() {
275
+ // No cache in mock
276
+ }
277
+
278
+ /**
279
+ * Add computation results for dependency simulation.
280
+ * * @param {string} computationName
281
+ * @param {Object} results - { entityId: result }
282
+ */
283
+ addComputationResults(computationName, results) {
284
+ this.computationResults[computationName] = results;
285
+ }
286
+
287
+ /**
288
+ * Get query log for inspection.
289
+ */
290
+ getQueryLog() {
291
+ return [...this.queryLog];
292
+ }
293
+
294
+ // =========================================================================
295
+ // HELPERS
296
+ // =========================================================================
297
+
298
+ _isEmpty(data) {
299
+ if (!data) return true;
300
+ if (Array.isArray(data)) return data.length === 0;
301
+ if (typeof data === 'object') return Object.keys(data).length === 0;
302
+ return false;
303
+ }
304
+ }
305
+
306
+ module.exports = { MockDataFetcher };