bulltrackers-module 1.0.768 → 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 (51) hide show
  1. package/functions/computation-system-v2/UserPortfolioMetrics.js +50 -0
  2. package/functions/computation-system-v2/computations/BehavioralAnomaly.js +557 -337
  3. package/functions/computation-system-v2/computations/GlobalAumPerAsset30D.js +103 -0
  4. package/functions/computation-system-v2/computations/PIDailyAssetAUM.js +134 -0
  5. package/functions/computation-system-v2/computations/PiFeatureVectors.js +227 -0
  6. package/functions/computation-system-v2/computations/PiRecommender.js +359 -0
  7. package/functions/computation-system-v2/computations/SignedInUserList.js +51 -0
  8. package/functions/computation-system-v2/computations/SignedInUserMirrorHistory.js +138 -0
  9. package/functions/computation-system-v2/computations/SignedInUserPIProfileMetrics.js +106 -0
  10. package/functions/computation-system-v2/computations/SignedInUserProfileMetrics.js +324 -0
  11. package/functions/computation-system-v2/config/bulltrackers.config.js +30 -128
  12. package/functions/computation-system-v2/core-api.js +17 -9
  13. package/functions/computation-system-v2/data_schema_reference.MD +108 -0
  14. package/functions/computation-system-v2/devtools/builder/builder.js +362 -0
  15. package/functions/computation-system-v2/devtools/builder/examples/user-metrics.yaml +26 -0
  16. package/functions/computation-system-v2/devtools/index.js +36 -0
  17. package/functions/computation-system-v2/devtools/shared/MockDataFactory.js +235 -0
  18. package/functions/computation-system-v2/devtools/shared/SchemaTemplates.js +475 -0
  19. package/functions/computation-system-v2/devtools/shared/SystemIntrospector.js +517 -0
  20. package/functions/computation-system-v2/devtools/shared/index.js +16 -0
  21. package/functions/computation-system-v2/devtools/simulation/DAGAnalyzer.js +243 -0
  22. package/functions/computation-system-v2/devtools/simulation/MockDataFetcher.js +306 -0
  23. package/functions/computation-system-v2/devtools/simulation/MockStorageManager.js +336 -0
  24. package/functions/computation-system-v2/devtools/simulation/SimulationEngine.js +525 -0
  25. package/functions/computation-system-v2/devtools/simulation/SimulationServer.js +581 -0
  26. package/functions/computation-system-v2/devtools/simulation/index.js +17 -0
  27. package/functions/computation-system-v2/devtools/simulation/simulate.js +324 -0
  28. package/functions/computation-system-v2/devtools/vscode-computation/package.json +90 -0
  29. package/functions/computation-system-v2/devtools/vscode-computation/snippets/computation.json +128 -0
  30. package/functions/computation-system-v2/devtools/vscode-computation/src/extension.ts +401 -0
  31. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/codeActions.ts +152 -0
  32. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/completions.ts +207 -0
  33. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/diagnostics.ts +205 -0
  34. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/hover.ts +205 -0
  35. package/functions/computation-system-v2/devtools/vscode-computation/tsconfig.json +22 -0
  36. package/functions/computation-system-v2/docs/HowToCreateComputations.MD +602 -0
  37. package/functions/computation-system-v2/framework/data/DataFetcher.js +250 -184
  38. package/functions/computation-system-v2/framework/data/MaterializedViewManager.js +84 -0
  39. package/functions/computation-system-v2/framework/data/QueryBuilder.js +38 -38
  40. package/functions/computation-system-v2/framework/execution/Orchestrator.js +215 -129
  41. package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +17 -19
  42. package/functions/computation-system-v2/framework/storage/StateRepository.js +32 -2
  43. package/functions/computation-system-v2/framework/storage/StorageManager.js +105 -67
  44. package/functions/computation-system-v2/framework/testing/ComputationTester.js +12 -6
  45. package/functions/computation-system-v2/handlers/dispatcher.js +57 -29
  46. package/functions/computation-system-v2/legacy/PiAssetRecommender.js.old +115 -0
  47. package/functions/computation-system-v2/legacy/PiSimilarityMatrix.js +104 -0
  48. package/functions/computation-system-v2/legacy/PiSimilarityVector.js +71 -0
  49. package/functions/computation-system-v2/scripts/debug_aggregation.js +25 -0
  50. package/functions/computation-system-v2/scripts/test-invalidation-scenarios.js +234 -0
  51. package/package.json +1 -1
@@ -0,0 +1,475 @@
1
+ /**
2
+ * @fileoverview Schema Templates
3
+ * * Realistic data templates for mock data generation.
4
+ * Based on actual BigQuery data from data_schema_reference.MD
5
+ */
6
+
7
+ // =========================================================================
8
+ // SHARED DATA & HELPERS
9
+ // =========================================================================
10
+
11
+ // Fixed set of instruments matching the style in data_schema_reference.MD
12
+ const MOCK_INSTRUMENTS = [
13
+ { id: 1001, ticker: 'AAPL', sector: 'Technology' },
14
+ { id: 1002, ticker: 'GOOGL', sector: 'Technology' },
15
+ { id: 1003, ticker: 'MSFT', sector: 'Technology' },
16
+ { id: 1004, ticker: 'TSLA', sector: 'Consumer Cyclical' },
17
+ { id: 1005, ticker: 'NVDA', sector: 'Technology' },
18
+ { id: 1006, ticker: 'JPM', sector: 'Financial Services' },
19
+ { id: 1007, ticker: 'JNJ', sector: 'Healthcare' },
20
+ { id: 1008, ticker: 'XOM', sector: 'Energy' },
21
+ { id: 1009, ticker: 'PG', sector: 'Consumer Defensive' },
22
+ { id: 1010, ticker: 'SPY', sector: 'ETF' },
23
+ { id: 1137, ticker: 'BTC', sector: 'Crypto' },
24
+ { id: 1250, ticker: 'EURUSD', sector: 'Currencies' },
25
+ { id: 2380, ticker: 'ZPHR.L', sector: 'Energy' },
26
+ { id: 9808, ticker: 'ZPTA', sector: 'Technology' }
27
+ ];
28
+
29
+ // Helper to get a deterministic instrument based on an ID (for mapping tables)
30
+ function getInstrFromEntity(entityId) {
31
+ let hash = 0;
32
+ const str = String(entityId);
33
+ for (let i = 0; i < str.length; i++) {
34
+ hash = ((hash << 5) - hash) + str.charCodeAt(i);
35
+ hash |= 0;
36
+ }
37
+ const index = Math.abs(hash) % MOCK_INSTRUMENTS.length;
38
+ return MOCK_INSTRUMENTS[index];
39
+ }
40
+
41
+ // Helper to get a consistent cash % for a user
42
+ function getCashPct(entityId) {
43
+ let hash = 0;
44
+ const str = String(entityId);
45
+ for (let i = 0; i < str.length; i++) {
46
+ hash = ((hash << 5) - hash) + str.charCodeAt(i);
47
+ hash |= 0;
48
+ }
49
+ return (Math.abs(hash) % 500) / 100 + 0.5;
50
+ }
51
+
52
+ const SchemaTemplates = {
53
+ // =========================================================================
54
+ // 1. PORTFOLIO SNAPSHOTS
55
+ // =========================================================================
56
+ portfolio_snapshots: {
57
+ columns: [
58
+ { name: 'date', type: 'DATE' },
59
+ { name: 'user_id', type: 'STRING' },
60
+ { name: 'user_type', type: 'STRING' },
61
+ { name: 'portfolio_data', type: 'JSON' },
62
+ { name: 'fetched_at', type: 'TIMESTAMP' }
63
+ ],
64
+ generators: {
65
+ user_type: () => randomChoice(['SIGNED_IN_USER', 'POPULAR_INVESTOR'])
66
+ },
67
+ jsonTemplates: {
68
+ portfolio_data: {
69
+ AggregatedMirrors: () => [],
70
+ AggregatedPositions: (entityId, count = null) => {
71
+ const n = count ?? 5;
72
+ const cashPct = getCashPct(entityId);
73
+ const equityPct = 100.0 - cashPct;
74
+
75
+ // Generate random raw weights
76
+ const rawWeights = Array.from({ length: n }, () => Math.random());
77
+ const totalRaw = rawWeights.reduce((a, b) => a + b, 0);
78
+
79
+ return rawWeights.map((w, i) => {
80
+ // Pick varied instruments using offset
81
+ // This ensures the portfolio has diverse holdings
82
+ const instrIndex = (parseInt(String(entityId).substring(0, 5)) + i) % MOCK_INSTRUMENTS.length;
83
+ const instr = MOCK_INSTRUMENTS[instrIndex];
84
+
85
+ const invested = (w / totalRaw) * equityPct;
86
+
87
+ return {
88
+ Direction: randomChoice(['Buy', 'Sell']),
89
+ InstrumentID: instr.id,
90
+ Invested: Number(invested.toFixed(6)),
91
+ NetProfit: randomFloat(-50, 150),
92
+ Value: randomFloat(100, 5000)
93
+ };
94
+ });
95
+ },
96
+ AggregatedPositionsByInstrumentTypeID: () => [
97
+ { Direction: 'Buy', InstrumentTypeID: 5, Invested: 92.01, NetProfit: 33.75, Value: 92.04 },
98
+ { Direction: 'Buy', InstrumentTypeID: 6, Invested: 7.37, NetProfit: 35.86, Value: 7.49 }
99
+ ],
100
+ AggregatedPositionsByStockIndustryID: () => [],
101
+ CreditByRealizedEquity: (entityId) => Number(getCashPct(entityId).toFixed(6)),
102
+ CreditByUnrealizedEquity: (entityId) => Number(getCashPct(entityId).toFixed(6)),
103
+ cid: (entityId) => String(entityId),
104
+ fetchedAt: () => new Date().toISOString(),
105
+ username: (entityId) => `User_${entityId}`
106
+ }
107
+ }
108
+ },
109
+
110
+ // ... (pi_ratings, pi_rankings, social_post_snapshots, trade_history_snapshots, watchlist_membership, pi_page_views, pi_master_list, pi_alert_history remain unchanged from your previous version) ...
111
+ // Note: I'm omitting the middle tables for brevity as they were correct in your previous upload,
112
+ // but MAKE SURE they are present in the final file. The key change is the EXPORTS at the bottom.
113
+
114
+ // =========================================================================
115
+ // 2. PI RATINGS
116
+ // =========================================================================
117
+ pi_ratings: {
118
+ columns: [
119
+ { name: 'date', type: 'DATE' },
120
+ { name: 'pi_id', type: 'STRING' },
121
+ { name: 'average_rating', type: 'FLOAT' },
122
+ { name: 'total_ratings', type: 'INTEGER' },
123
+ { name: 'ratings_by_user', type: 'JSON' },
124
+ { name: 'reviews', type: 'JSON' },
125
+ { name: 'last_updated', type: 'TIMESTAMP' }
126
+ ],
127
+ generators: {
128
+ average_rating: () => 5.0,
129
+ total_ratings: () => 1
130
+ },
131
+ jsonTemplates: {
132
+ ratings_by_user: () => ({ "29312236": 5 }),
133
+ reviews: () => [{
134
+ action: "edit",
135
+ actualUserCid: 29312236,
136
+ comment: "Great performance.",
137
+ createdAt: randomPastDate(30).toISOString(),
138
+ isAnonymous: false,
139
+ isImpersonating: false,
140
+ loggedAt: new Date().toISOString(),
141
+ piCid: 31075566,
142
+ rating: 5,
143
+ reviewId: "29312236_31075566",
144
+ reviewerUsername: "marau2021",
145
+ updatedAt: new Date().toISOString(),
146
+ userCid: 29312236
147
+ }]
148
+ }
149
+ },
150
+
151
+ // =========================================================================
152
+ // 3. SECTOR MAPPINGS
153
+ // =========================================================================
154
+ sector_mappings: {
155
+ columns: [
156
+ { name: 'symbol', type: 'STRING' },
157
+ { name: 'sector', type: 'STRING' }
158
+ ],
159
+ generators: {
160
+ symbol: (entityId) => getInstrFromEntity(entityId).ticker,
161
+ sector: (entityId) => getInstrFromEntity(entityId).sector
162
+ }
163
+ },
164
+
165
+ // =========================================================================
166
+ // 4. PI RANKINGS
167
+ // =========================================================================
168
+ pi_rankings: {
169
+ columns: [
170
+ { name: 'date', type: 'DATE' },
171
+ { name: 'pi_id', type: 'STRING' },
172
+ { name: 'username', type: 'STRING' },
173
+ { name: 'rank', type: 'INTEGER' },
174
+ { name: 'category', type: 'STRING' },
175
+ { name: 'rankings_data', type: 'JSON' },
176
+ { name: 'fetched_at', type: 'TIMESTAMP' }
177
+ ],
178
+ generators: {
179
+ rank: () => randomInt(1, 1000),
180
+ category: () => null,
181
+ username: (entityId) => `User_${entityId}`
182
+ },
183
+ jsonTemplates: {
184
+ rankings_data: {
185
+ AUMTier: () => randomInt(1, 6),
186
+ AUMTierDesc: () => randomChoice(["$50K-$100K", "$100K-$300K"]),
187
+ AUMValue: () => randomFloat(50000, 300000),
188
+ ActiveWeeks: () => randomInt(50, 200),
189
+ ActiveWeeksPct: () => 100,
190
+ Copiers: () => randomInt(50, 500),
191
+ CopiersGain: () => randomFloat(0, 5),
192
+ Country: () => randomChoice(['Taiwan', 'UK', 'Germany']),
193
+ CustomerId: (entityId) => parseInt(entityId),
194
+ DailyDD: () => randomFloat(-5, 0),
195
+ DailyGain: () => randomFloat(0, 1),
196
+ Exposure: () => randomFloat(60, 90),
197
+ Gain: () => randomFloat(10, 200),
198
+ RiskScore: () => randomInt(3, 6),
199
+ Trades: () => randomInt(20, 100),
200
+ UserName: (entityId) => `User_${entityId}`,
201
+ WinRatio: () => randomFloat(60, 80),
202
+ WeeklyDD: () => randomFloat(-10, 0),
203
+ PeakToValley: () => randomFloat(-25, -5),
204
+ PopularInvestor: () => true,
205
+ Verified: () => true
206
+ }
207
+ }
208
+ },
209
+
210
+ // =========================================================================
211
+ // 5. SOCIAL POST SNAPSHOTS
212
+ // =========================================================================
213
+ social_post_snapshots: {
214
+ columns: [
215
+ { name: 'date', type: 'DATE' },
216
+ { name: 'user_id', type: 'STRING' },
217
+ { name: 'user_type', type: 'STRING' },
218
+ { name: 'posts_data', type: 'JSON' },
219
+ { name: 'fetched_at', type: 'TIMESTAMP' }
220
+ ],
221
+ generators: {
222
+ user_type: () => 'POPULAR_INVESTOR'
223
+ },
224
+ jsonTemplates: {
225
+ posts_data: {
226
+ cid: (entityId) => String(entityId),
227
+ fetchedAt: () => new Date().toISOString(),
228
+ postCount: () => randomInt(1, 10),
229
+ posts: () => {
230
+ const posts = {};
231
+ const id = randomUUID();
232
+ posts[id] = {
233
+ id: id,
234
+ createdAt: randomPastDate(10).toISOString(),
235
+ fetchedAt: new Date().toISOString(),
236
+ stats: { comments: randomInt(0, 5), likes: randomInt(0, 50) },
237
+ text: "Market update: We are seeing strong growth in the tech sector. $NVDA $AAPL"
238
+ };
239
+ return posts;
240
+ }
241
+ }
242
+ }
243
+ },
244
+
245
+ // =========================================================================
246
+ // 6. TICKER MAPPINGS
247
+ // =========================================================================
248
+ ticker_mappings: {
249
+ columns: [
250
+ { name: 'instrument_id', type: 'INTEGER' },
251
+ { name: 'ticker', type: 'STRING' },
252
+ { name: 'last_updated', type: 'TIMESTAMP' }
253
+ ],
254
+ generators: {
255
+ instrument_id: (entityId) => getInstrFromEntity(entityId).id,
256
+ ticker: (entityId) => getInstrFromEntity(entityId).ticker
257
+ }
258
+ },
259
+
260
+ // =========================================================================
261
+ // 7. TRADE HISTORY SNAPSHOTS
262
+ // =========================================================================
263
+ trade_history_snapshots: {
264
+ columns: [
265
+ { name: 'date', type: 'DATE' },
266
+ { name: 'user_id', type: 'STRING' },
267
+ { name: 'user_type', type: 'STRING' },
268
+ { name: 'history_data', type: 'JSON' },
269
+ { name: 'fetched_at', type: 'TIMESTAMP' }
270
+ ],
271
+ generators: {
272
+ user_type: () => 'POPULAR_INVESTOR'
273
+ },
274
+ jsonTemplates: {
275
+ history_data: {
276
+ PublicHistoryPositions: () => {
277
+ return Array.from({ length: 5 }, () => ({
278
+ CID: randomInt(1000000, 9999999),
279
+ CloseDateTime: randomPastDate(60).toISOString(),
280
+ CloseRate: randomFloat(50, 500),
281
+ CloseReason: 0,
282
+ InstrumentID: randomChoice(MOCK_INSTRUMENTS).id,
283
+ IsBuy: true,
284
+ Leverage: 1,
285
+ NetProfit: randomFloat(-20, 100),
286
+ OpenDateTime: randomPastDate(365).toISOString(),
287
+ OpenRate: randomFloat(50, 500),
288
+ PositionID: randomInt(2000000000, 3000000000)
289
+ }));
290
+ },
291
+ cid: (entityId) => String(entityId),
292
+ fetchedAt: () => new Date().toISOString()
293
+ }
294
+ }
295
+ },
296
+
297
+ // =========================================================================
298
+ // 8. WATCHLIST MEMBERSHIP
299
+ // =========================================================================
300
+ watchlist_membership: {
301
+ columns: [
302
+ { name: 'date', type: 'DATE' },
303
+ { name: 'pi_id', type: 'STRING' },
304
+ { name: 'total_users', type: 'INTEGER' },
305
+ { name: 'public_watchlist_count', type: 'INTEGER' },
306
+ { name: 'private_watchlist_count', type: 'INTEGER' },
307
+ { name: 'users', type: 'JSON' },
308
+ { name: 'last_updated', type: 'TIMESTAMP' }
309
+ ],
310
+ generators: {
311
+ total_users: () => 1,
312
+ public_watchlist_count: () => null,
313
+ private_watchlist_count: () => 1
314
+ },
315
+ jsonTemplates: {
316
+ users: () => ["29312236"]
317
+ }
318
+ },
319
+
320
+ // =========================================================================
321
+ // 9. PI PAGE VIEWS
322
+ // =========================================================================
323
+ pi_page_views: {
324
+ columns: [
325
+ { name: 'date', type: 'DATE' },
326
+ { name: 'pi_id', type: 'STRING' },
327
+ { name: 'total_views', type: 'INTEGER' },
328
+ { name: 'unique_viewers', type: 'INTEGER' },
329
+ { name: 'views_by_user', type: 'JSON' },
330
+ { name: 'last_updated', type: 'TIMESTAMP' }
331
+ ],
332
+ generators: {
333
+ total_views: () => 9,
334
+ unique_viewers: () => 1
335
+ },
336
+ jsonTemplates: {
337
+ views_by_user: () => ({
338
+ "29312236": {
339
+ lastViewed: { _nanoseconds: 0, _seconds: Math.floor(Date.now() / 1000) },
340
+ viewCount: 9
341
+ }
342
+ })
343
+ }
344
+ },
345
+
346
+ // =========================================================================
347
+ // 10. PI MASTER LIST
348
+ // =========================================================================
349
+ pi_master_list: {
350
+ columns: [
351
+ { name: 'cid', type: 'STRING' },
352
+ { name: 'username', type: 'STRING' },
353
+ { name: 'first_seen_at', type: 'TIMESTAMP' },
354
+ { name: 'last_seen_at', type: 'TIMESTAMP' },
355
+ { name: 'last_updated', type: 'TIMESTAMP' }
356
+ ],
357
+ generators: {
358
+ username: (entityId) => `User_${entityId}`
359
+ }
360
+ },
361
+
362
+ // =========================================================================
363
+ // 11. PI ALERT HISTORY
364
+ // =========================================================================
365
+ pi_alert_history: {
366
+ columns: [
367
+ { name: 'date', type: 'DATE' },
368
+ { name: 'pi_id', type: 'STRING' },
369
+ { name: 'alert_type', type: 'STRING' },
370
+ { name: 'triggered', type: 'BOOLEAN' },
371
+ { name: 'trigger_count', type: 'INTEGER' },
372
+ { name: 'triggered_for', type: 'JSON' },
373
+ { name: 'metadata', type: 'JSON' },
374
+ { name: 'last_triggered', type: 'TIMESTAMP' },
375
+ { name: 'last_updated', type: 'TIMESTAMP' }
376
+ ],
377
+ generators: {
378
+ alert_type: () => 'lastUpdated',
379
+ triggered: () => false,
380
+ trigger_count: () => 0
381
+ },
382
+ jsonTemplates: {
383
+ triggered_for: () => [],
384
+ metadata: () => ({})
385
+ }
386
+ },
387
+
388
+ // =========================================================================
389
+ // 12. ASSET PRICES
390
+ // =========================================================================
391
+ asset_prices: {
392
+ columns: [
393
+ { name: 'date', type: 'DATE' },
394
+ { name: 'instrument_id', type: 'INTEGER' },
395
+ { name: 'ticker', type: 'STRING' },
396
+ { name: 'price', type: 'FLOAT' },
397
+ { name: 'open', type: 'FLOAT' },
398
+ { name: 'high', type: 'FLOAT' },
399
+ { name: 'low', type: 'FLOAT' },
400
+ { name: 'close', type: 'FLOAT' },
401
+ { name: 'volume', type: 'INTEGER' },
402
+ { name: 'fetched_at', type: 'TIMESTAMP' }
403
+ ],
404
+ generators: {
405
+ instrument_id: (entityId) => getInstrFromEntity(entityId).id,
406
+ ticker: (entityId) => getInstrFromEntity(entityId).ticker,
407
+ price: () => randomFloat(50, 500),
408
+ close: () => randomFloat(50, 500)
409
+ }
410
+ },
411
+
412
+ // =========================================================================
413
+ // COMPUTATION RESULTS
414
+ // =========================================================================
415
+ computation_results: {
416
+ columns: [
417
+ { name: 'date', type: 'DATE' },
418
+ { name: 'computation_name', type: 'STRING' },
419
+ { name: 'category', type: 'STRING' },
420
+ { name: 'entity_id', type: 'STRING' },
421
+ { name: 'result', type: 'JSON' },
422
+ { name: 'hash', type: 'STRING' },
423
+ { name: 'created_at', type: 'TIMESTAMP' }
424
+ ],
425
+ generators: {
426
+ hash: () => randomHex(16)
427
+ }
428
+ }
429
+ };
430
+
431
+ // =========================================================================
432
+ // HELPERS
433
+ // =========================================================================
434
+
435
+ function randomInt(min, max) {
436
+ return Math.floor(Math.random() * (max - min + 1)) + min;
437
+ }
438
+
439
+ function randomFloat(min, max, decimals = 2) {
440
+ const val = Math.random() * (max - min) + min;
441
+ return Number(val.toFixed(decimals));
442
+ }
443
+
444
+ function randomChoice(arr) {
445
+ return arr[Math.floor(Math.random() * arr.length)];
446
+ }
447
+
448
+ function randomUsername() {
449
+ return `User_${randomInt(1000, 9999)}`;
450
+ }
451
+
452
+ function randomPastDate(maxDaysAgo) {
453
+ const date = new Date();
454
+ date.setDate(date.getDate() - randomInt(1, maxDaysAgo));
455
+ return date;
456
+ }
457
+
458
+ function randomUUID() {
459
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
460
+ const r = Math.random() * 16 | 0;
461
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
462
+ return v.toString(16);
463
+ });
464
+ }
465
+
466
+ function randomHex(length) {
467
+ return Array.from({ length }, () => Math.floor(Math.random() * 16).toString(16)).join('');
468
+ }
469
+
470
+ // **KEY CHANGE**: Export MOCK_INSTRUMENTS so SimulationEngine can use it
471
+ module.exports = {
472
+ SchemaTemplates,
473
+ MOCK_INSTRUMENTS,
474
+ helpers: { randomInt, randomFloat, randomChoice, randomUsername, randomPastDate, randomUUID, randomHex }
475
+ };