bulltrackers-module 1.0.104 → 1.0.106

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 (33) hide show
  1. package/README.MD +222 -222
  2. package/functions/appscript-api/helpers/errors.js +19 -19
  3. package/functions/appscript-api/index.js +58 -58
  4. package/functions/computation-system/helpers/orchestration_helpers.js +647 -113
  5. package/functions/computation-system/utils/data_loader.js +191 -191
  6. package/functions/computation-system/utils/utils.js +149 -254
  7. package/functions/core/utils/firestore_utils.js +433 -433
  8. package/functions/core/utils/pubsub_utils.js +53 -53
  9. package/functions/dispatcher/helpers/dispatch_helpers.js +47 -47
  10. package/functions/dispatcher/index.js +52 -52
  11. package/functions/etoro-price-fetcher/helpers/handler_helpers.js +124 -124
  12. package/functions/fetch-insights/helpers/handler_helpers.js +91 -91
  13. package/functions/generic-api/helpers/api_helpers.js +379 -379
  14. package/functions/generic-api/index.js +150 -150
  15. package/functions/invalid-speculator-handler/helpers/handler_helpers.js +75 -75
  16. package/functions/orchestrator/helpers/discovery_helpers.js +226 -226
  17. package/functions/orchestrator/helpers/update_helpers.js +92 -92
  18. package/functions/orchestrator/index.js +147 -147
  19. package/functions/price-backfill/helpers/handler_helpers.js +116 -123
  20. package/functions/social-orchestrator/helpers/orchestrator_helpers.js +61 -61
  21. package/functions/social-task-handler/helpers/handler_helpers.js +288 -288
  22. package/functions/task-engine/handler_creator.js +78 -78
  23. package/functions/task-engine/helpers/discover_helpers.js +125 -125
  24. package/functions/task-engine/helpers/update_helpers.js +118 -118
  25. package/functions/task-engine/helpers/verify_helpers.js +162 -162
  26. package/functions/task-engine/utils/firestore_batch_manager.js +258 -258
  27. package/index.js +105 -113
  28. package/package.json +45 -45
  29. package/functions/computation-system/computation_dependencies.json +0 -120
  30. package/functions/computation-system/helpers/worker_helpers.js +0 -340
  31. package/functions/computation-system/utils/computation_state_manager.js +0 -178
  32. package/functions/computation-system/utils/dependency_graph.js +0 -191
  33. package/functions/speculator-cleanup-orchestrator/helpers/cleanup_helpers.js +0 -160
@@ -1,255 +1,150 @@
1
- /**
2
- * @fileoverview Computation system sub-pipes and utils.
3
- * REFACTORED: Now stateless and receive dependencies where needed.
4
- * DYNAMIC: Categorization logic is now an exported function.
5
- */
6
-
7
- const { FieldValue, FieldPath } = require('@google-cloud/firestore');
8
-
9
- // --- REMOVED ---
10
- // const { utils } = require('aiden-shared-calculations-unified');
11
- // const { withRetry } = utils;
12
- // --- END REMOVED ---
13
-
14
-
15
- /**
16
- * NEW FUNCTION: This logic is now wrapped and exported.
17
- * It receives the 'calculations' object from the entry point.
18
- * @param {object} calculations - The full calculations object from the 'aiden-shared-calculations-unified' package.
19
- * @returns {object} An object containing categorized calculations and name sets.
20
- */
21
- function categorizeCalculations(calculations) {
22
- const HISTORICAL_CALC_NAMES = new Set();
23
- const META_CALC_NAMES = new Set();
24
- const BACKTEST_CALC_NAMES = new Set(); // <-- ADD THIS
25
-
26
- const historicalCalculations = {};
27
- const dailyCalculations = {};
28
- const metaCalculations = {};
29
- const backtestCalculations = {}; // <-- ADD THIS
30
-
31
- for (const category in calculations) {
32
- // 1. Check for 'meta' category first (by top-level directory name)
33
- if (category === 'meta') {
34
- if (!metaCalculations[category]) metaCalculations[category] = {};
35
- for (const calcName in calculations[category]) {
36
- const CalculationClass = calculations[category][calcName];
37
- metaCalculations[category][calcName] = CalculationClass;
38
- META_CALC_NAMES.add(calcName);
39
- }
40
- continue; // Done with this category
41
- }
42
-
43
- // 2. Check for 'backtests' category
44
- if (category === 'backtests') { // <-- ADD THIS BLOCK
45
- if (!backtestCalculations[category]) backtestCalculations[category] = {};
46
- for (const calcName in calculations[category]) {
47
- const CalculationClass = calculations[category][calcName];
48
- backtestCalculations[category][calcName] = CalculationClass;
49
- BACKTEST_CALC_NAMES.add(calcName);
50
- }
51
- continue;
52
- }
53
-
54
-
55
- // 3. Process other categories (e.g., 'pnl', 'capital_flow')
56
- for (const subKey in calculations[category]) {
57
- const item = calculations[category][subKey];
58
-
59
- // Check if the key is 'historical' and it contains an object of calculations
60
- if (subKey === 'historical' && typeof item === 'object' && item !== null && !Array.isArray(item)) {
61
- // This is the historical subdirectory, e.g., calculations.capital_flow.historical
62
-
63
- if (!historicalCalculations[category]) historicalCalculations[category] = {};
64
-
65
- for (const calcName in item) {
66
- const CalculationClass = item[calcName];
67
- // Add it to the historical list, using the parent 'category'
68
- historicalCalculations[category][calcName] = CalculationClass;
69
- HISTORICAL_CALC_NAMES.add(calcName);
70
- }
71
- }
72
- // Check if the item is a function (a standard daily calc at the root of the category)
73
- else if (typeof item === 'function') {
74
- // This is a standard daily calc, e.g., calculations.pnl.average-daily-pnl
75
- const calcName = subKey;
76
- const CalculationClass = item;
77
-
78
- if (!dailyCalculations[category]) dailyCalculations[category] = {};
79
- dailyCalculations[category][calcName] = CalculationClass;
80
- }
81
- }
82
- }
83
-
84
- return {
85
- historicalCalculations,
86
- dailyCalculations,
87
- metaCalculations,
88
- backtestCalculations, // <-- ADD THIS
89
- HISTORICAL_CALC_NAMES,
90
- META_CALC_NAMES,
91
- BACKTEST_CALC_NAMES // <-- ADD THIS
92
- };
93
- }
94
- // --- End Dynamic Categorization ---
95
-
96
-
97
- /**
98
- * Sub-pipe: pipe.computationSystem.computationUtils.commitBatchInChunks
99
- * @param {object} config - The computation system configuration object.
100
- * @param {object} dependencies - Contains db, logger, calculationUtils.
101
- * @param {Array<object>} writes - Array of { ref: DocumentReference, data: object }.
102
- * @param {string} operationName - Name for logging.
103
- */
104
- async function commitBatchInChunks(config, dependencies, writes, operationName) {
105
- // --- MODIFIED: Get withRetry from dependencies ---
106
- const { db, logger, calculationUtils } = dependencies;
107
- const { withRetry } = calculationUtils;
108
- // --- END MODIFIED ---
109
-
110
- const batchSizeLimit = config.batchSizeLimit || 450;
111
-
112
- if (writes.length === 0) {
113
- logger.log('WARN', `[${operationName}] No writes to commit.`);
114
- return;
115
- }
116
- for (let i = 0; i < writes.length; i += batchSizeLimit) {
117
- const batch = db.batch(); // Use db
118
- const chunk = writes.slice(i, i + batchSizeLimit);
119
- chunk.forEach(write => batch.set(write.ref, write.data, { merge: true }));
120
-
121
- const chunkNum = Math.floor(i / batchSizeLimit) + 1;
122
- const totalChunks = Math.ceil(writes.length / batchSizeLimit);
123
- await withRetry(
124
- () => batch.commit(),
125
- `${operationName} (Chunk ${chunkNum}/${totalChunks})`
126
- );
127
- logger.log('INFO', `[${operationName}] Committed chunk ${chunkNum}/${totalChunks} (${chunk.length} ops).`);
128
- }
129
- }
130
-
131
- /**
132
- * Sub-pipe: pipe.computationSystem.computationUtils.getExpectedDateStrings
133
- * (Stateless)
134
- */
135
- function getExpectedDateStrings(startDate, endDate) {
136
- const dateStrings = [];
137
- if (startDate <= endDate) {
138
- const startUTC = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
139
- const endUTC = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth(), endDate.getUTCDate()));
140
- for (let d = startUTC; d <= endUTC; d.setUTCDate(d.getUTCDate() + 1)) {
141
- dateStrings.push(new Date(d).toISOString().slice(0, 10));
142
- }
143
- }
144
- return dateStrings;
145
- }
146
-
147
-
148
- /**
149
- * Sub-pipe: pipe.computationSystem.computationUtils.processJobsInParallel
150
- * (Stateless, as taskFunction will receive dependencies)
151
- */
152
- async function processJobsInParallel(jobs, taskFunction, passName, config) {
153
- // This function itself doesn't need dependencies,
154
- // but the 'taskFunction' it calls *will* receive them from its caller.
155
- const { logger } = require("sharedsetup")(__filename); // Use local logger for this static util
156
- const results = [];
157
- const maxConcurrentDates = config.maxConcurrentDates || 3;
158
- if (jobs.length > 0) {
159
- logger.log('INFO', `[${passName}] Processing ${jobs.length} jobs with ${maxConcurrentDates} parallel workers.`);
160
- jobs.sort((a, b) => new Date(a.date) - new Date(b.date));
161
- for (let i = 0; i < jobs.length; i += maxConcurrentDates) {
162
- const jobBatch = jobs.slice(i, i + maxConcurrentDates);
163
- const promises = jobBatch.map(job => taskFunction(new Date(job.date + 'T00:00:00Z'), job.missing));
164
- results.push(...await Promise.allSettled(promises));
165
- }
166
- } else {
167
- logger.log('INFO', `[${passName}] No jobs to process.`);
168
- }
169
- return results;
170
- }
171
-
172
- /**
173
- * Internal helper: Finds the earliest date document in a collection.
174
- */
175
- async function getFirstDateFromCollection(config, dependencies, collectionName) {
176
- // --- MODIFIED: Get withRetry from dependencies ---
177
- const { db, logger, calculationUtils } = dependencies;
178
- const { withRetry } = calculationUtils;
179
- // --- END MODIFIED ---
180
- let earliestDate = null;
181
- try {
182
- const blockDocRefs = await withRetry(
183
- () => db.collection(collectionName).listDocuments(), // Use db
184
- `GetBlocks(${collectionName})`
185
- );
186
-
187
- if (blockDocRefs.length === 0) {
188
- logger.log('WARN', `No block documents found in collection: ${collectionName}`);
189
- return null;
190
- }
191
-
192
- for (const blockDocRef of blockDocRefs) {
193
- const snapshotQuery = blockDocRef.collection(config.snapshotsSubcollection)
194
- .where(FieldPath.documentId(), '>=', '2000-01-01')
195
- .orderBy(FieldPath.documentId(), 'asc')
196
- .limit(1);
197
-
198
- const snapshotSnap = await withRetry(
199
- () => snapshotQuery.get(),
200
- `GetEarliestSnapshot(${blockDocRef.path})`
201
- );
202
-
203
- if (!snapshotSnap.empty && /^\d{4}-\d{2}-\d{2}$/.test(snapshotSnap.docs[0].id)) {
204
- const foundDate = new Date(snapshotSnap.docs[0].id + 'T00:00:00Z');
205
- if (!earliestDate || foundDate < earliestDate) {
206
- earliestDate = foundDate;
207
- }
208
- }
209
- }
210
-
211
- } catch (e) {
212
- logger.log('ERROR', `GetFirstDate failed for ${collectionName}`, { errorMessage: e.message });
213
- }
214
- return earliestDate;
215
- }
216
-
217
- /**
218
- * Sub-pipe: pipe.computationSystem.computationUtils.getFirstDateFromSourceData
219
- * @param {object} config - The computation system configuration object.
220
- * @param {object} dependencies - Contains db, logger, calculationUtils.
221
- * @returns {Promise<Date>} The earliest date found or a default fallback date.
222
- */
223
- async function getFirstDateFromSourceData(config, dependencies) {
224
- const { logger } = dependencies;
225
- logger.log('INFO', 'Querying for the earliest date from source portfolio data...');
226
-
227
- // Pass dependencies to sub-pipe
228
- const investorDate = await getFirstDateFromCollection(config, dependencies, config.normalUserPortfolioCollection);
229
- const speculatorDate = await getFirstDateFromCollection(config, dependencies, config.speculatorPortfolioCollection);
230
-
231
- let earliestDate;
232
- if (investorDate && speculatorDate) {
233
- earliestDate = investorDate < speculatorDate ? investorDate : speculatorDate;
234
- } else {
235
- earliestDate = investorDate || speculatorDate;
236
- }
237
-
238
- if (earliestDate) {
239
- logger.log('INFO', `Found earliest source data date: ${earliestDate.toISOString().slice(0, 10)}`);
240
- return earliestDate;
241
- } else {
242
- const fallbackDate = new Date(config.earliestComputationDate + 'T00:00:00Z' || '2023-01-01T00:00:00Z');
243
- logger.log('WARN', `No source data found. Defaulting first date to: ${fallbackDate.toISOString().slice(0, 10)}`);
244
- return fallbackDate;
245
- }
246
- }
247
-
248
- module.exports = {
249
- FieldValue, FieldPath,
250
- // unifiedUtils: utils, // This is no longer defined here
251
- categorizeCalculations, // EXPORT THE NEW FUNCTION
252
- // withRetry, // This is no longer defined here
253
- commitBatchInChunks,
254
- getExpectedDateStrings, processJobsInParallel, getFirstDateFromSourceData,
1
+ /**
2
+ * @fileoverview Computation system sub-pipes and utils.
3
+ * REFACTORED: Now stateless and receive dependencies where needed.
4
+ * DYNAMIC: Categorization logic is removed, replaced by manifest.
5
+ */
6
+
7
+ const { FieldValue, FieldPath } = require('@google-cloud/firestore');
8
+
9
+ /**
10
+ * Normalizes a calculation name to kebab-case.
11
+ * @param {string} name
12
+ * @returns {string}
13
+ */
14
+ function normalizeName(name) {
15
+ return name.replace(/_/g, '-');
16
+ }
17
+
18
+ /**
19
+ * Sub-pipe: pipe.computationSystem.computationUtils.commitBatchInChunks
20
+ * @param {object} config - The computation system configuration object.
21
+ * @param {object} dependencies - Contains db, logger, calculationUtils.
22
+ * @param {Array<object>} writes - Array of { ref: DocumentReference, data: object }.
23
+ * @param {string} operationName - Name for logging.
24
+ */
25
+ async function commitBatchInChunks(config, dependencies, writes, operationName) {
26
+ // --- MODIFIED: Get withRetry from dependencies ---
27
+ const { db, logger, calculationUtils } = dependencies;
28
+ const { withRetry } = calculationUtils;
29
+ // --- END MODIFIED ---
30
+
31
+ const batchSizeLimit = config.batchSizeLimit || 450;
32
+
33
+ if (writes.length === 0) {
34
+ logger.log('WARN', `[${operationName}] No writes to commit.`);
35
+ return;
36
+ }
37
+ for (let i = 0; i < writes.length; i += batchSizeLimit) {
38
+ const batch = db.batch(); // Use db
39
+ const chunk = writes.slice(i, i + batchSizeLimit);
40
+ chunk.forEach(write => batch.set(write.ref, write.data, { merge: true }));
41
+
42
+ const chunkNum = Math.floor(i / batchSizeLimit) + 1;
43
+ const totalChunks = Math.ceil(writes.length / batchSizeLimit);
44
+ await withRetry(
45
+ () => batch.commit(),
46
+ `${operationName} (Chunk ${chunkNum}/${totalChunks})`
47
+ );
48
+ logger.log('INFO', `[${operationName}] Committed chunk ${chunkNum}/${totalChunks} (${chunk.length} ops).`);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Sub-pipe: pipe.computationSystem.computationUtils.getExpectedDateStrings
54
+ * (Stateless)
55
+ */
56
+ function getExpectedDateStrings(startDate, endDate) {
57
+ const dateStrings = [];
58
+ if (startDate <= endDate) {
59
+ const startUTC = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
60
+ const endUTC = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth(), endDate.getUTCDate()));
61
+ for (let d = startUTC; d <= endUTC; d.setUTCDate(d.getUTCDate() + 1)) {
62
+ dateStrings.push(new Date(d).toISOString().slice(0, 10));
63
+ }
64
+ }
65
+ return dateStrings;
66
+ }
67
+
68
+ /**
69
+ * Internal helper: Finds the earliest date document in a collection.
70
+ */
71
+ async function getFirstDateFromCollection(config, dependencies, collectionName) {
72
+ // --- MODIFIED: Get withRetry from dependencies ---
73
+ const { db, logger, calculationUtils } = dependencies;
74
+ const { withRetry } = calculationUtils;
75
+ // --- END MODIFIED ---
76
+ let earliestDate = null;
77
+ try {
78
+ const blockDocRefs = await withRetry(
79
+ () => db.collection(collectionName).listDocuments(), // Use db
80
+ `GetBlocks(${collectionName})`
81
+ );
82
+
83
+ if (blockDocRefs.length === 0) {
84
+ logger.log('WARN', `No block documents found in collection: ${collectionName}`);
85
+ return null;
86
+ }
87
+
88
+ for (const blockDocRef of blockDocRefs) {
89
+ const snapshotQuery = blockDocRef.collection(config.snapshotsSubcollection)
90
+ .where(FieldPath.documentId(), '>=', '2000-01-01')
91
+ .orderBy(FieldPath.documentId(), 'asc')
92
+ .limit(1);
93
+
94
+ const snapshotSnap = await withRetry(
95
+ () => snapshotQuery.get(),
96
+ `GetEarliestSnapshot(${blockDocRef.path})`
97
+ );
98
+
99
+ if (!snapshotSnap.empty && /^\d{4}-\d{2}-\d{2}$/.test(snapshotSnap.docs[0].id)) {
100
+ const foundDate = new Date(snapshotSnap.docs[0].id + 'T00:00:00Z');
101
+ if (!earliestDate || foundDate < earliestDate) {
102
+ earliestDate = foundDate;
103
+ }
104
+ }
105
+ }
106
+
107
+ } catch (e) {
108
+ logger.log('ERROR', `GetFirstDate failed for ${collectionName}`, { errorMessage: e.message });
109
+ }
110
+ return earliestDate;
111
+ }
112
+
113
+ /**
114
+ * Sub-pipe: pipe.computationSystem.computationUtils.getFirstDateFromSourceData
115
+ * @param {object} config - The computation system configuration object.
116
+ * @param {object} dependencies - Contains db, logger, calculationUtils.
117
+ * @returns {Promise<Date>} The earliest date found or a default fallback date.
118
+ */
119
+ async function getFirstDateFromSourceData(config, dependencies) {
120
+ const { logger } = dependencies;
121
+ logger.log('INFO', 'Querying for the earliest date from source portfolio data...');
122
+
123
+ // Pass dependencies to sub-pipe
124
+ const investorDate = await getFirstDateFromCollection(config, dependencies, config.normalUserPortfolioCollection);
125
+ const speculatorDate = await getFirstDateFromCollection(config, dependencies, config.speculatorPortfolioCollection);
126
+
127
+ let earliestDate;
128
+ if (investorDate && speculatorDate) {
129
+ earliestDate = investorDate < speculatorDate ? investorDate : speculatorDate;
130
+ } else {
131
+ earliestDate = investorDate || speculatorDate;
132
+ }
133
+
134
+ if (earliestDate) {
135
+ logger.log('INFO', `Found earliest source data date: ${earliestDate.toISOString().slice(0, 10)}`);
136
+ return earliestDate;
137
+ } else {
138
+ const fallbackDate = new Date(config.earliestComputationDate + 'T00:00:00Z' || '2023-01-01T00:00:00Z');
139
+ logger.log('WARN', `No source data found. Defaulting first date to: ${fallbackDate.toISOString().slice(0, 10)}`);
140
+ return fallbackDate;
141
+ }
142
+ }
143
+
144
+ module.exports = {
145
+ FieldValue, FieldPath,
146
+ normalizeName,
147
+ commitBatchInChunks,
148
+ getExpectedDateStrings,
149
+ getFirstDateFromSourceData,
255
150
  };