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.
- package/README.MD +222 -222
- package/functions/appscript-api/helpers/errors.js +19 -19
- package/functions/appscript-api/index.js +58 -58
- package/functions/computation-system/helpers/orchestration_helpers.js +647 -113
- package/functions/computation-system/utils/data_loader.js +191 -191
- package/functions/computation-system/utils/utils.js +149 -254
- package/functions/core/utils/firestore_utils.js +433 -433
- package/functions/core/utils/pubsub_utils.js +53 -53
- package/functions/dispatcher/helpers/dispatch_helpers.js +47 -47
- package/functions/dispatcher/index.js +52 -52
- package/functions/etoro-price-fetcher/helpers/handler_helpers.js +124 -124
- package/functions/fetch-insights/helpers/handler_helpers.js +91 -91
- package/functions/generic-api/helpers/api_helpers.js +379 -379
- package/functions/generic-api/index.js +150 -150
- package/functions/invalid-speculator-handler/helpers/handler_helpers.js +75 -75
- package/functions/orchestrator/helpers/discovery_helpers.js +226 -226
- package/functions/orchestrator/helpers/update_helpers.js +92 -92
- package/functions/orchestrator/index.js +147 -147
- package/functions/price-backfill/helpers/handler_helpers.js +116 -123
- package/functions/social-orchestrator/helpers/orchestrator_helpers.js +61 -61
- package/functions/social-task-handler/helpers/handler_helpers.js +288 -288
- package/functions/task-engine/handler_creator.js +78 -78
- package/functions/task-engine/helpers/discover_helpers.js +125 -125
- package/functions/task-engine/helpers/update_helpers.js +118 -118
- package/functions/task-engine/helpers/verify_helpers.js +162 -162
- package/functions/task-engine/utils/firestore_batch_manager.js +258 -258
- package/index.js +105 -113
- package/package.json +45 -45
- package/functions/computation-system/computation_dependencies.json +0 -120
- package/functions/computation-system/helpers/worker_helpers.js +0 -340
- package/functions/computation-system/utils/computation_state_manager.js +0 -178
- package/functions/computation-system/utils/dependency_graph.js +0 -191
- package/functions/speculator-cleanup-orchestrator/helpers/cleanup_helpers.js +0 -160
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Main orchestration logic.
|
|
3
|
-
* REFACTORED: This file now contains the main pipe functions
|
|
4
|
-
* that are called by the Cloud Function entry points.
|
|
5
|
-
* They receive all dependencies.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { checkDiscoveryNeed, getDiscoveryCandidates, dispatchDiscovery } = require('./helpers/discovery_helpers');
|
|
9
|
-
const { getUpdateTargets, dispatchUpdates } = require('./helpers/update_helpers');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Main pipe for running the complete discovery orchestration process.
|
|
14
|
-
* This is the function the Cloud Function entry point will call.
|
|
15
|
-
* @param {object} config - The full orchestratorConfig object.
|
|
16
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
17
|
-
* @returns {Promise<void>}
|
|
18
|
-
*/
|
|
19
|
-
async function runDiscoveryOrchestrator(config, dependencies) {
|
|
20
|
-
const { logger, firestoreUtils } = dependencies;
|
|
21
|
-
logger.log('INFO', '🚀 Discovery Orchestrator triggered via module...');
|
|
22
|
-
|
|
23
|
-
// Call sub-pipe from core
|
|
24
|
-
await firestoreUtils.resetProxyLocks(dependencies, config);
|
|
25
|
-
|
|
26
|
-
// Call sub-pipe for 'normal'
|
|
27
|
-
// Pass the directly required sub-pipe functions implicitly via runDiscovery call context
|
|
28
|
-
await runDiscovery('normal', config.discoveryConfig.normal, config, dependencies);
|
|
29
|
-
|
|
30
|
-
// Call sub-pipe for 'speculator'
|
|
31
|
-
await runDiscovery('speculator', config.discoveryConfig.speculator, config, dependencies);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Main pipe for running the complete update orchestration process.
|
|
36
|
-
* This is the function the Cloud Function entry point will call.
|
|
37
|
-
* @param {object} config - The full orchestratorConfig object.
|
|
38
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
39
|
-
* @returns {Promise<void>}
|
|
40
|
-
*/
|
|
41
|
-
async function runUpdateOrchestrator(config, dependencies) {
|
|
42
|
-
const { logger, firestoreUtils } = dependencies;
|
|
43
|
-
logger.log('INFO', '🚀 Update Orchestrator triggered via module...');
|
|
44
|
-
|
|
45
|
-
// Call sub-pipe from core
|
|
46
|
-
await firestoreUtils.resetProxyLocks(dependencies, config);
|
|
47
|
-
|
|
48
|
-
// Call sub-pipe for 'normal'
|
|
49
|
-
await runUpdates('normal', config.updateConfig, config, dependencies);
|
|
50
|
-
|
|
51
|
-
// Call sub-pipe for 'speculator'
|
|
52
|
-
await runUpdates('speculator', config.updateConfig, config, dependencies);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// --- Internal Helpers (which are also exposed as sub-pipes) ---
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Runs the discovery process for a *single* user type.
|
|
59
|
-
* This is an internal helper, but also maps to pipe.orchestrator.runDiscovery
|
|
60
|
-
* @param {string} userType - 'normal' or 'speculator'.
|
|
61
|
-
* @param {object} userTypeConfig - The specific config slice (e.g., config.discoveryConfig.normal).
|
|
62
|
-
* @param {object} globalConfig - Global config.
|
|
63
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
64
|
-
*/
|
|
65
|
-
async function runDiscovery(userType, userTypeConfig, globalConfig, dependencies) {
|
|
66
|
-
const { logger } = dependencies;
|
|
67
|
-
logger.log('INFO', `[Module Orchestrator] Starting discovery for ${userType} users...`);
|
|
68
|
-
|
|
69
|
-
// --- 3. Use the directly required functions ---
|
|
70
|
-
// 1. pipe.orchestrator.checkDiscoveryNeed -> becomes checkDiscoveryNeed
|
|
71
|
-
const { needsDiscovery, blocksToFill } = await checkDiscoveryNeed(
|
|
72
|
-
userType,
|
|
73
|
-
userTypeConfig,
|
|
74
|
-
dependencies
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
if (!needsDiscovery) {
|
|
78
|
-
logger.log('INFO', `[Module Orchestrator] No discovery needed for ${userType}.`);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 2. pipe.orchestrator.getDiscoveryCandidates -> becomes getDiscoveryCandidates
|
|
83
|
-
const candidates = await getDiscoveryCandidates(
|
|
84
|
-
userType,
|
|
85
|
-
blocksToFill,
|
|
86
|
-
userTypeConfig,
|
|
87
|
-
dependencies
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
// 3. pipe.orchestrator.dispatchDiscovery -> becomes dispatchDiscovery
|
|
91
|
-
await dispatchDiscovery(
|
|
92
|
-
userType,
|
|
93
|
-
candidates,
|
|
94
|
-
userTypeConfig,
|
|
95
|
-
dependencies
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
logger.log('SUCCESS', `[Module Orchestrator] Dispatched discovery tasks for ${userType}.`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Runs the update process for a *single* user type.
|
|
103
|
-
* This is an internal helper, but also maps to pipe.orchestrator.runUpdates
|
|
104
|
-
* @param {string} userType - 'normal' or 'speculator'.
|
|
105
|
-
* @param {object} updateConfig - The config.updateConfig object.
|
|
106
|
-
* @param {object} globalConfig - Global config.
|
|
107
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
108
|
-
*/
|
|
109
|
-
async function runUpdates(userType, updateConfig, globalConfig, dependencies) {
|
|
110
|
-
const { logger } = dependencies;
|
|
111
|
-
logger.log('INFO', `[Module Orchestrator] Collecting users for daily update (${userType})...`);
|
|
112
|
-
|
|
113
|
-
const now = new Date();
|
|
114
|
-
const startOfTodayUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
115
|
-
const DaysAgoUTC = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
116
|
-
|
|
117
|
-
const thresholds = {
|
|
118
|
-
dateThreshold: startOfTodayUTC,
|
|
119
|
-
gracePeriodThreshold: DaysAgoUTC
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
// --- 4. Use the directly required functions ---
|
|
123
|
-
// 1. pipe.orchestrator.getUpdateTargets -> becomes getUpdateTargets
|
|
124
|
-
const targets = await getUpdateTargets(
|
|
125
|
-
userType,
|
|
126
|
-
thresholds,
|
|
127
|
-
updateConfig,
|
|
128
|
-
dependencies
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// 2. pipe.orchestrator.dispatchUpdates -> becomes dispatchUpdates
|
|
132
|
-
await dispatchUpdates(
|
|
133
|
-
targets,
|
|
134
|
-
userType,
|
|
135
|
-
updateConfig,
|
|
136
|
-
dependencies
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
logger.log('SUCCESS', `[Module Orchestrator] Dispatched update tasks for ${userType}.`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
module.exports = {
|
|
143
|
-
runDiscoveryOrchestrator,
|
|
144
|
-
runUpdateOrchestrator,
|
|
145
|
-
// Exporting these internal helpers so the main index.js can map them
|
|
146
|
-
runDiscovery,
|
|
147
|
-
runUpdates
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Main orchestration logic.
|
|
3
|
+
* REFACTORED: This file now contains the main pipe functions
|
|
4
|
+
* that are called by the Cloud Function entry points.
|
|
5
|
+
* They receive all dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { checkDiscoveryNeed, getDiscoveryCandidates, dispatchDiscovery } = require('./helpers/discovery_helpers');
|
|
9
|
+
const { getUpdateTargets, dispatchUpdates } = require('./helpers/update_helpers');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Main pipe for running the complete discovery orchestration process.
|
|
14
|
+
* This is the function the Cloud Function entry point will call.
|
|
15
|
+
* @param {object} config - The full orchestratorConfig object.
|
|
16
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
*/
|
|
19
|
+
async function runDiscoveryOrchestrator(config, dependencies) {
|
|
20
|
+
const { logger, firestoreUtils } = dependencies;
|
|
21
|
+
logger.log('INFO', '🚀 Discovery Orchestrator triggered via module...');
|
|
22
|
+
|
|
23
|
+
// Call sub-pipe from core
|
|
24
|
+
await firestoreUtils.resetProxyLocks(dependencies, config);
|
|
25
|
+
|
|
26
|
+
// Call sub-pipe for 'normal'
|
|
27
|
+
// Pass the directly required sub-pipe functions implicitly via runDiscovery call context
|
|
28
|
+
await runDiscovery('normal', config.discoveryConfig.normal, config, dependencies);
|
|
29
|
+
|
|
30
|
+
// Call sub-pipe for 'speculator'
|
|
31
|
+
await runDiscovery('speculator', config.discoveryConfig.speculator, config, dependencies);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Main pipe for running the complete update orchestration process.
|
|
36
|
+
* This is the function the Cloud Function entry point will call.
|
|
37
|
+
* @param {object} config - The full orchestratorConfig object.
|
|
38
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
39
|
+
* @returns {Promise<void>}
|
|
40
|
+
*/
|
|
41
|
+
async function runUpdateOrchestrator(config, dependencies) {
|
|
42
|
+
const { logger, firestoreUtils } = dependencies;
|
|
43
|
+
logger.log('INFO', '🚀 Update Orchestrator triggered via module...');
|
|
44
|
+
|
|
45
|
+
// Call sub-pipe from core
|
|
46
|
+
await firestoreUtils.resetProxyLocks(dependencies, config);
|
|
47
|
+
|
|
48
|
+
// Call sub-pipe for 'normal'
|
|
49
|
+
await runUpdates('normal', config.updateConfig, config, dependencies);
|
|
50
|
+
|
|
51
|
+
// Call sub-pipe for 'speculator'
|
|
52
|
+
await runUpdates('speculator', config.updateConfig, config, dependencies);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// --- Internal Helpers (which are also exposed as sub-pipes) ---
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Runs the discovery process for a *single* user type.
|
|
59
|
+
* This is an internal helper, but also maps to pipe.orchestrator.runDiscovery
|
|
60
|
+
* @param {string} userType - 'normal' or 'speculator'.
|
|
61
|
+
* @param {object} userTypeConfig - The specific config slice (e.g., config.discoveryConfig.normal).
|
|
62
|
+
* @param {object} globalConfig - Global config.
|
|
63
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
64
|
+
*/
|
|
65
|
+
async function runDiscovery(userType, userTypeConfig, globalConfig, dependencies) {
|
|
66
|
+
const { logger } = dependencies;
|
|
67
|
+
logger.log('INFO', `[Module Orchestrator] Starting discovery for ${userType} users...`);
|
|
68
|
+
|
|
69
|
+
// --- 3. Use the directly required functions ---
|
|
70
|
+
// 1. pipe.orchestrator.checkDiscoveryNeed -> becomes checkDiscoveryNeed
|
|
71
|
+
const { needsDiscovery, blocksToFill } = await checkDiscoveryNeed(
|
|
72
|
+
userType,
|
|
73
|
+
userTypeConfig,
|
|
74
|
+
dependencies
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!needsDiscovery) {
|
|
78
|
+
logger.log('INFO', `[Module Orchestrator] No discovery needed for ${userType}.`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 2. pipe.orchestrator.getDiscoveryCandidates -> becomes getDiscoveryCandidates
|
|
83
|
+
const candidates = await getDiscoveryCandidates(
|
|
84
|
+
userType,
|
|
85
|
+
blocksToFill,
|
|
86
|
+
userTypeConfig,
|
|
87
|
+
dependencies
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// 3. pipe.orchestrator.dispatchDiscovery -> becomes dispatchDiscovery
|
|
91
|
+
await dispatchDiscovery(
|
|
92
|
+
userType,
|
|
93
|
+
candidates,
|
|
94
|
+
userTypeConfig,
|
|
95
|
+
dependencies
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
logger.log('SUCCESS', `[Module Orchestrator] Dispatched discovery tasks for ${userType}.`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Runs the update process for a *single* user type.
|
|
103
|
+
* This is an internal helper, but also maps to pipe.orchestrator.runUpdates
|
|
104
|
+
* @param {string} userType - 'normal' or 'speculator'.
|
|
105
|
+
* @param {object} updateConfig - The config.updateConfig object.
|
|
106
|
+
* @param {object} globalConfig - Global config.
|
|
107
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
108
|
+
*/
|
|
109
|
+
async function runUpdates(userType, updateConfig, globalConfig, dependencies) {
|
|
110
|
+
const { logger } = dependencies;
|
|
111
|
+
logger.log('INFO', `[Module Orchestrator] Collecting users for daily update (${userType})...`);
|
|
112
|
+
|
|
113
|
+
const now = new Date();
|
|
114
|
+
const startOfTodayUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
115
|
+
const DaysAgoUTC = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
116
|
+
|
|
117
|
+
const thresholds = {
|
|
118
|
+
dateThreshold: startOfTodayUTC,
|
|
119
|
+
gracePeriodThreshold: DaysAgoUTC
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// --- 4. Use the directly required functions ---
|
|
123
|
+
// 1. pipe.orchestrator.getUpdateTargets -> becomes getUpdateTargets
|
|
124
|
+
const targets = await getUpdateTargets(
|
|
125
|
+
userType,
|
|
126
|
+
thresholds,
|
|
127
|
+
updateConfig,
|
|
128
|
+
dependencies
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// 2. pipe.orchestrator.dispatchUpdates -> becomes dispatchUpdates
|
|
132
|
+
await dispatchUpdates(
|
|
133
|
+
targets,
|
|
134
|
+
userType,
|
|
135
|
+
updateConfig,
|
|
136
|
+
dependencies
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
logger.log('SUCCESS', `[Module Orchestrator] Dispatched update tasks for ${userType}.`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = {
|
|
143
|
+
runDiscoveryOrchestrator,
|
|
144
|
+
runUpdateOrchestrator,
|
|
145
|
+
// Exporting these internal helpers so the main index.js can map them
|
|
146
|
+
runDiscovery,
|
|
147
|
+
runUpdates
|
|
148
148
|
};
|
|
@@ -1,123 +1,116 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Main pipe: pipe.maintenance.runBackfillAssetPrices
|
|
3
|
-
* A one-time function to backfill historical price data from eToro's
|
|
4
|
-
* candle API into the new sharded `asset_prices` collection.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { FieldValue } = require('@google-cloud/firestore'); // Todo inject this
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
await Promise.all(promises);
|
|
120
|
-
await headerManager.flushPerformanceUpdates();
|
|
121
|
-
|
|
122
|
-
logger.log('SUCCESS', `[PriceBackfill] Backfill complete. Success: ${successCount}, Failed: ${errorCount}`);
|
|
123
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Main pipe: pipe.maintenance.runBackfillAssetPrices
|
|
3
|
+
* A one-time function to backfill historical price data from eToro's
|
|
4
|
+
* candle API into the new sharded `asset_prices` collection.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { FieldValue } = require('@google-cloud/firestore'); // Todo inject this
|
|
8
|
+
|
|
9
|
+
const pLimit = require('p-limit'); // TODO inject this
|
|
10
|
+
|
|
11
|
+
// How many tickers to fetch in parallel
|
|
12
|
+
const CONCURRENT_REQUESTS = 10;
|
|
13
|
+
// How many days of history to fetch
|
|
14
|
+
const DAYS_TO_FETCH = 365;
|
|
15
|
+
// How many tickers to group into one Firestore document
|
|
16
|
+
const SHARD_SIZE = 40;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Main pipe: pipe.maintenance.runBackfillAssetPrices
|
|
20
|
+
* @param {object} config - Configuration object.
|
|
21
|
+
* @param {object} dependencies - Contains db, logger, headerManager, proxyManager, calculationUtils.
|
|
22
|
+
*/
|
|
23
|
+
// --- MODIFIED: Removed calculationUtils, as it's in dependencies ---
|
|
24
|
+
exports.runBackfillAssetPrices = async (config, dependencies) => {
|
|
25
|
+
const { db, logger, headerManager, proxyManager, calculationUtils } = dependencies;
|
|
26
|
+
const { loadInstrumentMappings } = calculationUtils; // <-- Get function from dependencies
|
|
27
|
+
// --- END MODIFIED ---
|
|
28
|
+
|
|
29
|
+
logger.log('INFO', '[PriceBackfill] Starting historical price backfill...');
|
|
30
|
+
|
|
31
|
+
let mappings;
|
|
32
|
+
try {
|
|
33
|
+
// --- MODIFIED: Use the injected utils ---
|
|
34
|
+
mappings = await loadInstrumentMappings();
|
|
35
|
+
// --- END MODIFIED ---
|
|
36
|
+
if (!mappings || !mappings.instrumentToTicker) {
|
|
37
|
+
throw new Error("Failed to load instrument mappings.");
|
|
38
|
+
}
|
|
39
|
+
} catch (e) {
|
|
40
|
+
logger.log('ERROR', '[PriceBackfill] Could not load instrument mappings.', { err: e.message });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const instrumentIds = Object.keys(mappings.instrumentToTicker);
|
|
45
|
+
logger.log('INFO', `[PriceBackfill] Found ${instrumentIds.length} instruments to backfill.`);
|
|
46
|
+
|
|
47
|
+
const limit = pLimit(CONCURRENT_REQUESTS);
|
|
48
|
+
let successCount = 0;
|
|
49
|
+
let errorCount = 0;
|
|
50
|
+
|
|
51
|
+
const promises = instrumentIds.map(instrumentId => {
|
|
52
|
+
return limit(async () => {
|
|
53
|
+
try {
|
|
54
|
+
const ticker = mappings.instrumentToTicker[instrumentId] || `unknown_${instrumentId}`;
|
|
55
|
+
const url = `https://candle.etoro.com/candles/asc.json/OneDay/${DAYS_TO_FETCH}/${instrumentId}`; //TODO implement config value
|
|
56
|
+
|
|
57
|
+
const selectedHeader = await headerManager.selectHeader();
|
|
58
|
+
let wasSuccess = false;
|
|
59
|
+
|
|
60
|
+
const response = await proxyManager.fetch(url, {
|
|
61
|
+
headers: selectedHeader.header,
|
|
62
|
+
timeout: 20000
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
throw new Error(`API error ${response.status} for instrument ${instrumentId}`);
|
|
67
|
+
}
|
|
68
|
+
wasSuccess = true;
|
|
69
|
+
headerManager.updatePerformance(selectedHeader.id, wasSuccess);
|
|
70
|
+
|
|
71
|
+
const data = await response.json();
|
|
72
|
+
const candles = data?.Candles?.[0]?.Candles;
|
|
73
|
+
|
|
74
|
+
if (!Array.isArray(candles) || candles.length === 0) {
|
|
75
|
+
logger.log('WARN', `[PriceBackfill] No candle data returned for ${ticker} (${instrumentId})`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Format data as a map
|
|
80
|
+
const prices = {};
|
|
81
|
+
for (const candle of candles) {
|
|
82
|
+
const dateKey = candle.FromDate.substring(0, 10);
|
|
83
|
+
prices[dateKey] = candle.Close;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Determine shard ID
|
|
87
|
+
const shardId = `shard_${parseInt(instrumentId, 10) % SHARD_SIZE}`;
|
|
88
|
+
const docRef = db.collection('asset_prices').doc(shardId); // TODO implement config value
|
|
89
|
+
|
|
90
|
+
const payload = {
|
|
91
|
+
[instrumentId]: {
|
|
92
|
+
ticker: ticker,
|
|
93
|
+
prices: prices,
|
|
94
|
+
lastUpdated: FieldValue.serverTimestamp()
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Write to Firestore
|
|
99
|
+
await docRef.set(payload, { merge: true });
|
|
100
|
+
logger.log('TRACE', `[PriceBackfill] Successfully stored data for ${ticker} (${instrumentId}) in ${shardId}`);
|
|
101
|
+
successCount++;
|
|
102
|
+
|
|
103
|
+
} catch (err) {
|
|
104
|
+
logger.log('ERROR', `[PriceBackfill] Failed to process instrument ${instrumentId}`, { err: err.message });
|
|
105
|
+
errorCount++;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await Promise.all(promises);
|
|
111
|
+
|
|
112
|
+
// Flush any remaining header updates
|
|
113
|
+
await headerManager.flushPerformanceUpdates();
|
|
114
|
+
|
|
115
|
+
logger.log('SUCCESS', `[PriceBackfill] Backfill complete. Success: ${successCount}, Failed: ${errorCount}`);
|
|
116
|
+
};
|