bulltrackers-module 1.0.171 → 1.0.172
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/functions/computation-system/controllers/computation_controller.js +215 -0
- package/functions/computation-system/helpers/orchestration_helpers.js +189 -718
- package/functions/computation-system/layers/math_primitives.js +346 -0
- package/functions/task-engine/helpers/update_helpers.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Control Layer - Orchestrates Computation Execution
|
|
3
|
+
* UPDATE: Imported and exposed HistoryExtractor in the math context.
|
|
4
|
+
* UPDATE: Implemented strict User Type segregation. 'All' now defaults to 'Normal'.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
DataExtractor,
|
|
9
|
+
HistoryExtractor,
|
|
10
|
+
MathPrimitives,
|
|
11
|
+
Aggregators,
|
|
12
|
+
Validators,
|
|
13
|
+
SCHEMAS,
|
|
14
|
+
SignalPrimitives
|
|
15
|
+
} = require('../layers/math_primitives');
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
loadDailyInsights,
|
|
19
|
+
loadDailySocialPostInsights,
|
|
20
|
+
getPortfolioPartRefs,
|
|
21
|
+
getHistoryPartRefs,
|
|
22
|
+
streamPortfolioData,
|
|
23
|
+
streamHistoryData
|
|
24
|
+
} = require('../utils/data_loader');
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// DATA LOADER WRAPPER
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
class DataLoader {
|
|
31
|
+
constructor(config, dependencies) {
|
|
32
|
+
this.config = config;
|
|
33
|
+
this.deps = dependencies;
|
|
34
|
+
this.cache = { mappings: null, insights: new Map(), social: new Map() };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async loadMappings() {
|
|
38
|
+
if (this.cache.mappings) return this.cache.mappings;
|
|
39
|
+
const { calculationUtils } = this.deps;
|
|
40
|
+
this.cache.mappings = await calculationUtils.loadInstrumentMappings();
|
|
41
|
+
return this.cache.mappings;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async loadInsights(dateStr) {
|
|
45
|
+
if (this.cache.insights.has(dateStr)) return this.cache.insights.get(dateStr);
|
|
46
|
+
const insights = await loadDailyInsights(this.config, this.deps, dateStr);
|
|
47
|
+
this.cache.insights.set(dateStr, insights);
|
|
48
|
+
return insights;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async loadSocial(dateStr) {
|
|
52
|
+
if (this.cache.social.has(dateStr)) return this.cache.social.get(dateStr);
|
|
53
|
+
const social = await loadDailySocialPostInsights(this.config, this.deps, dateStr);
|
|
54
|
+
this.cache.social.set(dateStr, social);
|
|
55
|
+
return social;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// CONTEXT BUILDER
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
class ContextBuilder {
|
|
64
|
+
static buildPerUserContext(options) {
|
|
65
|
+
const {
|
|
66
|
+
todayPortfolio, yesterdayPortfolio, todayHistory, yesterdayHistory,
|
|
67
|
+
userId, userType, dateStr, metadata, mappings, insights, socialData,
|
|
68
|
+
computedDependencies, config, deps
|
|
69
|
+
} = options;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
// User Identity & Data
|
|
73
|
+
user: {
|
|
74
|
+
id: userId,
|
|
75
|
+
type: userType,
|
|
76
|
+
portfolio: { today: todayPortfolio, yesterday: yesterdayPortfolio },
|
|
77
|
+
history: { today: todayHistory, yesterday: yesterdayHistory }
|
|
78
|
+
},
|
|
79
|
+
// Global Time & Data
|
|
80
|
+
date: { today: dateStr },
|
|
81
|
+
insights: { today: insights?.today, yesterday: insights?.yesterday },
|
|
82
|
+
social: { today: socialData?.today, yesterday: socialData?.yesterday },
|
|
83
|
+
// Helpers
|
|
84
|
+
mappings: mappings || {},
|
|
85
|
+
math: {
|
|
86
|
+
extract: DataExtractor,
|
|
87
|
+
history: HistoryExtractor,
|
|
88
|
+
compute: MathPrimitives,
|
|
89
|
+
aggregate: Aggregators,
|
|
90
|
+
validate: Validators,
|
|
91
|
+
signals: SignalPrimitives,
|
|
92
|
+
schemas: SCHEMAS
|
|
93
|
+
},
|
|
94
|
+
computed: computedDependencies || {},
|
|
95
|
+
meta: metadata,
|
|
96
|
+
config,
|
|
97
|
+
deps
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static buildMetaContext(options) {
|
|
102
|
+
const {
|
|
103
|
+
dateStr, metadata, mappings, insights, socialData,
|
|
104
|
+
computedDependencies, config, deps
|
|
105
|
+
} = options;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
date: { today: dateStr },
|
|
109
|
+
insights: { today: insights?.today, yesterday: insights?.yesterday },
|
|
110
|
+
social: { today: socialData?.today, yesterday: socialData?.yesterday },
|
|
111
|
+
mappings: mappings || {},
|
|
112
|
+
math: {
|
|
113
|
+
extract: DataExtractor,
|
|
114
|
+
history: HistoryExtractor,
|
|
115
|
+
compute: MathPrimitives,
|
|
116
|
+
aggregate: Aggregators,
|
|
117
|
+
validate: Validators,
|
|
118
|
+
signals: SignalPrimitives,
|
|
119
|
+
schemas: SCHEMAS
|
|
120
|
+
},
|
|
121
|
+
computed: computedDependencies || {},
|
|
122
|
+
meta: metadata,
|
|
123
|
+
config,
|
|
124
|
+
deps
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// EXECUTOR
|
|
131
|
+
// ============================================================================
|
|
132
|
+
|
|
133
|
+
class ComputationExecutor {
|
|
134
|
+
constructor(config, dependencies, dataLoader) {
|
|
135
|
+
this.config = config;
|
|
136
|
+
this.deps = dependencies;
|
|
137
|
+
this.loader = dataLoader;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async executePerUser(calcInstance, metadata, dateStr, portfolioData, historyData, computedDeps) {
|
|
141
|
+
const { logger } = this.deps;
|
|
142
|
+
|
|
143
|
+
// --------------------------------------------------------------------
|
|
144
|
+
// 1. DETERMINE TARGET SCHEMA
|
|
145
|
+
// --------------------------------------------------------------------
|
|
146
|
+
// We strictly enforce separation here.
|
|
147
|
+
// Unless 'speculator' is explicitly requested, we default to 'normal'.
|
|
148
|
+
// This effectively deprecates 'all' by treating it as 'normal' for safety.
|
|
149
|
+
const targetUserType = (metadata.userType === 'speculator')
|
|
150
|
+
? SCHEMAS.USER_TYPES.SPECULATOR
|
|
151
|
+
: SCHEMAS.USER_TYPES.NORMAL;
|
|
152
|
+
|
|
153
|
+
const mappings = await this.loader.loadMappings();
|
|
154
|
+
const insights = metadata.rootDataDependencies?.includes('insights')
|
|
155
|
+
? { today: await this.loader.loadInsights(dateStr) } : null;
|
|
156
|
+
|
|
157
|
+
// Loop through user batch
|
|
158
|
+
for (const [userId, todayPortfolio] of Object.entries(portfolioData)) {
|
|
159
|
+
const yesterdayPortfolio = historyData ? historyData[userId] : null;
|
|
160
|
+
|
|
161
|
+
// ----------------------------------------------------------------
|
|
162
|
+
// 2. IDENTIFY ACTUAL DATA TYPE
|
|
163
|
+
// ----------------------------------------------------------------
|
|
164
|
+
// We inspect the data structure to know what we are holding.
|
|
165
|
+
const actualUserType = todayPortfolio.PublicPositions
|
|
166
|
+
? SCHEMAS.USER_TYPES.SPECULATOR
|
|
167
|
+
: SCHEMAS.USER_TYPES.NORMAL;
|
|
168
|
+
|
|
169
|
+
// ----------------------------------------------------------------
|
|
170
|
+
// 3. STRICT GATEKEEPING
|
|
171
|
+
// ----------------------------------------------------------------
|
|
172
|
+
// If the computation asked for 'normal' (or 'all'), but we have a 'speculator', SKIP.
|
|
173
|
+
// If the computation asked for 'speculator', but we have a 'normal', SKIP.
|
|
174
|
+
if (targetUserType !== actualUserType) continue;
|
|
175
|
+
|
|
176
|
+
const context = ContextBuilder.buildPerUserContext({
|
|
177
|
+
todayPortfolio, yesterdayPortfolio,
|
|
178
|
+
userId, userType: actualUserType, dateStr, metadata, mappings, insights,
|
|
179
|
+
computedDependencies: computedDeps, config: this.config, deps: this.deps
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
await calcInstance.process(context);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
logger.log('WARN', `Calc ${metadata.name} failed for user ${userId}: ${e.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async executeOncePerDay(calcInstance, metadata, dateStr, computedDeps) {
|
|
191
|
+
const mappings = await this.loader.loadMappings();
|
|
192
|
+
const insights = metadata.rootDataDependencies?.includes('insights')
|
|
193
|
+
? { today: await this.loader.loadInsights(dateStr) } : null;
|
|
194
|
+
const social = metadata.rootDataDependencies?.includes('social')
|
|
195
|
+
? { today: await this.loader.loadSocial(dateStr) } : null;
|
|
196
|
+
|
|
197
|
+
const context = ContextBuilder.buildMetaContext({
|
|
198
|
+
dateStr, metadata, mappings, insights, socialData: social,
|
|
199
|
+
computedDependencies: computedDeps, config: this.config, deps: this.deps
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return await calcInstance.process(context);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class ComputationController {
|
|
207
|
+
constructor(config, dependencies) {
|
|
208
|
+
this.config = config;
|
|
209
|
+
this.deps = dependencies;
|
|
210
|
+
this.loader = new DataLoader(config, dependencies);
|
|
211
|
+
this.executor = new ComputationExecutor(config, dependencies, this.loader);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = { ComputationController };
|