aiden-shared-calculations-unified 1.0.146 → 1.0.148

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.
@@ -0,0 +1,1200 @@
1
+ # Computation System Developer Guide
2
+
3
+ ## Overview
4
+
5
+ This guide explains how to create computations for the BullTrackers system. You write the logic, we run it. Understanding these static methods is all you need to get started.
6
+
7
+ ---
8
+
9
+ ## Required Static Methods
10
+
11
+ Every computation class must implement three static methods:
12
+
13
+ ### 1. `getMetadata()`
14
+
15
+ Defines **what** your computation is and **how** the system should run it.
16
+
17
+ **Returns:** Object with the following properties:
18
+
19
+ #### Core Properties
20
+
21
+ | Property | Type | Required | Description |
22
+ |----------|------|----------|-------------|
23
+ | `type` | `'standard'` \| `'meta'` | ✅ | **Standard**: Runs once per user (portfolio analysis, user scoring). **Meta**: Runs once per day globally (market aggregates, leaderboards). |
24
+ | `category` | `string` | ✅ | Organizational folder (e.g., `'profiling'`, `'signals'`, `'analytics'`). Used for storage paths and reports. |
25
+
26
+ #### Scheduling & Execution
27
+
28
+ | Property | Type | Default | Description |
29
+ |----------|------|---------|-------------|
30
+ | `schedule` | `object` | Daily | Controls when your computation runs:<br>• `{ type: 'DAILY' }` - Every day<br>• `{ type: 'WEEKLY', days: [1, 5] }` - Monday & Friday (0=Sun, 6=Sat)<br>• `{ type: 'MONTHLY', days: [1, 15] }` - 1st and 15th of month |
31
+ | `isHistorical` | `boolean` | `false` | If `true`, receives yesterday's result in `context.previousComputed[yourName]` for chaining (e.g., cumulative scores). |
32
+ | `isTest` | `boolean` | `false` | If `true`, **always re-runs on current day** to test system changes. Use for debug probes only. |
33
+
34
+ #### Data Dependencies
35
+
36
+ | Property | Type | Default | Description |
37
+ |----------|------|---------|-------------|
38
+ | `rootDataDependencies` | `string[]` | `[]` | What data you need. Options:<br>• `'portfolio'` - User positions<br>• `'history'` - Trade history<br>• `'social'` - Social posts<br>• `'insights'` - Market sentiment<br>• `'price'` - Asset prices<br>• `'rankings'` - PI leaderboard<br>• `'verification'` - Verified user profiles<br>• `'ratings'`, `'pageViews'`, `'watchlist'`, `'alerts'` - Profile metrics |
39
+ | `userType` | `string` | `'all'` | Filter users: `'normal'`, `'speculator'`, `'popular_investor'`, `'signed_in_user'`, `'all'` |
40
+ | `canHaveMissingRoots` | `boolean` | `false` | If `true`, runs even when some root data is missing. Use for fault-tolerant computations. |
41
+ | `mandatoryRoots` | `string[]` | `[]` | Subset of `rootDataDependencies` that **must** exist. Overrides permissive flags. Example: `['portfolio']` when ratings are optional. |
42
+
43
+ #### Advanced Features
44
+
45
+ | Property | Type | Default | Description |
46
+ |----------|------|---------|-------------|
47
+ | `rootDataSeries` | `object` | `null` | Load **historical** root data for lookbacks:<br>`{ insights: 7, alerts: 30 }`<br>Access via `context.globalData.series.root.insights['2024-01-15']` |
48
+ | `dependencySeries` | `object` | `null` | Load **past computation results**:<br>`{ 'RiskScore': 7 }`<br>Access via `context.globalData.series.results['2024-01-15']['RiskScore']` |
49
+ | `ttlDays` | `number` | `90` | Data retention (days from computation date). Set to `365` for yearly reports, `30` for ephemeral analytics. |
50
+ | `isPage` | `boolean` | `false` | If `true`, stores each user's result in a **subcollection** (`pages/{cid}`) instead of a single document. Use for large per-user datasets (>10K users). |
51
+ | `isAlertComputation` | `boolean` | `false` | If `true`, triggers Firestore listeners for real-time alerts. System automatically publishes to Pub/Sub on completion. |
52
+ | `targetCid` | `string` | `null` | Debug Only. Limits execution to a single User CID. Useful for testing logic on a specific user without running the full batch. |
53
+
54
+ ---
55
+
56
+ ### 2. `getDependencies()`
57
+
58
+ Lists other computations your code needs.
59
+
60
+ **Returns:** `string[]` - Array of computation names (kebab-case)
61
+
62
+ **Example:**
63
+ ```javascript
64
+ static getDependencies() {
65
+ return ['portfolio-risk-score', 'sentiment-analyzer'];
66
+ }
67
+ ```
68
+
69
+ **Access in `process()`:**
70
+ ```javascript
71
+ const riskScore = context.computed['portfolio-risk-score'][userId];
72
+ ```
73
+
74
+ If `isHistorical: true`, you also get:
75
+ ```javascript
76
+ const yesterdayRisk = context.previousComputed['portfolio-risk-score'][userId];
77
+ ```
78
+
79
+ ---
80
+
81
+ ### 3. `getSchema()`
82
+
83
+ Defines the **shape** of your output for validation.
84
+
85
+ **Returns:** JSON Schema object
86
+
87
+ **Example:**
88
+ ```javascript
89
+ static getSchema() {
90
+ return {
91
+ type: 'object',
92
+ patternProperties: {
93
+ '^[0-9]+$': { // User IDs
94
+ type: 'object',
95
+ properties: {
96
+ score: { type: 'number' },
97
+ label: { type: 'string' },
98
+ confidence: { type: 'number' }
99
+ }
100
+ }
101
+ }
102
+ };
103
+ }
104
+ ```
105
+
106
+ **Purpose:** The system learns expected bounds (e.g., scores between 0-100) and blocks outputs that violate physics (NaN, extreme outliers).
107
+
108
+ ---
109
+
110
+ ## Context API Reference
111
+
112
+ Your `process(context)` receives:
113
+
114
+ ### For Standard Computations
115
+ ```javascript
116
+ {
117
+ user: {
118
+ id: '12345',
119
+ type: 'speculator',
120
+ portfolio: { today: {...}, yesterday: {...} },
121
+ history: { today: {...}, yesterday: {...} },
122
+ verification: {...}, // If rootDataDependencies includes 'verification'
123
+ rankEntry: {...}, // User's ranking entry
124
+ rankEntryYesterday: {...}
125
+ },
126
+ date: { today: '2024-01-15' },
127
+ mappings: { instrumentToTicker: {...}, instrumentToSector: {...} },
128
+ math: { extract: {...}, compute: {...}, ... }, // Helper functions
129
+ computed: { 'other-calculation': { userId: {...} } },
130
+ previousComputed: { ... }, // If isHistorical: true
131
+ globalData: {
132
+ rankings: [...], // All PIs
133
+ verifications: {...}, // All verified users
134
+ series: { // If rootDataSeries/dependencySeries defined
135
+ root: { insights: { '2024-01-10': {...} } },
136
+ results: { '2024-01-10': { 'RiskScore': {...} } }
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ ### For Meta Computations
143
+ ```javascript
144
+ {
145
+ date: { today: '2024-01-15' },
146
+ mappings: {...},
147
+ prices: { history: {...} }, // If rootDataDependencies includes 'price'
148
+ computed: { 'user-calculation': { userId: {...} } }, // All users
149
+ globalData: {
150
+ rankings: [...],
151
+ verifications: {...}
152
+ }
153
+ }
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Mathematics Layer (`context.math`)
159
+
160
+ The system injects powerful helper functions via `context.math`. These are your building blocks.
161
+
162
+ ### Data Extraction (`context.math.extract` / `context.math.DataExtractor`)
163
+
164
+ **Purpose:** Safe accessors for raw data schemas. Handles edge cases automatically.
165
+
166
+ #### Portfolio Extraction
167
+
168
+ ```javascript
169
+ // Get user's positions (handles different user types automatically)
170
+ const positions = context.math.extract.getPositions(
171
+ context.user.portfolio.today,
172
+ context.user.type
173
+ );
174
+ // Returns: Array of position objects
175
+
176
+ // Extract position details
177
+ positions.forEach(pos => {
178
+ const instrumentId = context.math.extract.getInstrumentId(pos);
179
+ const pnl = context.math.extract.getNetProfit(pos);
180
+ const invested = context.math.extract.getPositionWeight(pos);
181
+ const value = context.math.extract.getPositionValue(pos);
182
+ const leverage = context.math.extract.getLeverage(pos);
183
+ const direction = context.math.extract.getDirection(pos); // "Buy" or "Sell"
184
+ const openRate = context.math.extract.getOpenRate(pos);
185
+ const currentRate = context.math.extract.getCurrentRate(pos);
186
+ const stopLoss = context.math.extract.getStopLossRate(pos);
187
+ const takeProfit = context.math.extract.getTakeProfitRate(pos);
188
+ const openDate = context.math.extract.getOpenDateTime(pos); // Date object
189
+ });
190
+
191
+ // Analyze portfolio composition
192
+ const sectors = context.math.extract.getCurrentSectors(
193
+ context.user.portfolio.today,
194
+ context.mappings,
195
+ context.user.type
196
+ );
197
+ // Returns: ['Technology', 'Healthcare', ...]
198
+
199
+ const aumPerAsset = context.math.extract.getAUMPerAsset(
200
+ context.user.portfolio.today,
201
+ context.user.type
202
+ );
203
+ // Returns: { instrumentId: aumValue, ... }
204
+ ```
205
+
206
+ #### Trade History Extraction
207
+
208
+ ```javascript
209
+ const trades = context.math.history.getTrades(context.user.history.today);
210
+ // Returns: Array of closed trade objects
211
+
212
+ const realizedEquity = context.math.history.getRealizedEquity(context.user.history.today);
213
+
214
+ // Analyze trading patterns
215
+ const summary = context.math.history.getSummary(context.user.history.today);
216
+ // Returns: {
217
+ // totalTrades: 150,
218
+ // winRatio: 62.5, // percentage
219
+ // avgProfit: 45.2,
220
+ // avgLoss: -32.1,
221
+ // avgHoldingTimeInMinutes: 1440
222
+ // }
223
+
224
+ const dailyHistory = context.math.history.getDailyHistory(context.user.history.today);
225
+ // Returns: [{
226
+ // date: '2024-01-15',
227
+ // netProfit: 123.45,
228
+ // investedVolume: 5000,
229
+ // tradesCount: 8
230
+ // }, ...]
231
+
232
+ const tradedAssets = context.math.history.getTradedAssets(context.user.history.today);
233
+ // Returns: [{
234
+ // instrumentId: 100,
235
+ // avgHoldingTimeInMinutes: 720
236
+ // }, ...]
237
+
238
+ const tradedSectors = context.math.history.getTradedSectors(
239
+ context.user.history.today,
240
+ context.mappings
241
+ );
242
+ // Returns: ['Technology', 'Energy', ...]
243
+
244
+ const copiedUsers = context.math.history.getCopiedUsers(context.user.history.today);
245
+ // Returns: [123456, 789012] // CIDs of copied PIs
246
+ ```
247
+
248
+ #### Insights Extraction (Market Sentiment)
249
+
250
+ ```javascript
251
+ const insights = context.math.insights.getInsights(context, 'today');
252
+ // Returns: Array of instrument insights
253
+
254
+ const btcInsight = context.math.insights.getInsightForInstrument(insights, 1); // BTC instrumentId
255
+ if (btcInsight) {
256
+ const owners = context.math.insights.getTotalOwners(btcInsight);
257
+ const longPct = context.math.insights.getLongPercent(btcInsight);
258
+ const shortPct = context.math.insights.getShortPercent(btcInsight);
259
+ const growth = context.math.insights.getGrowthPercent(btcInsight); // Day-over-day
260
+ }
261
+ ```
262
+
263
+ #### Rankings Extraction (Popular Investors)
264
+
265
+ ```javascript
266
+ const allRankings = context.globalData.rankings;
267
+
268
+ // Get user's ranking entry
269
+ const userRank = context.math.RankingsExtractor.getEntry(allRankings, context.user.id);
270
+ if (userRank) {
271
+ const gain = context.math.RankingsExtractor.getGain(userRank);
272
+ const riskScore = context.math.RankingsExtractor.getRiskScore(userRank);
273
+ const copiers = context.math.RankingsExtractor.getCopiers(userRank);
274
+ const aumTier = context.math.RankingsExtractor.getAUMTierDesc(userRank);
275
+ const aumValue = context.math.RankingsExtractor.getAUMValue(userRank);
276
+ const tags = context.math.RankingsExtractor.getTags(userRank); // ['Conservative', 'Crypto']
277
+ }
278
+ ```
279
+
280
+ #### Verification Extraction
281
+
282
+ ```javascript
283
+ const verifications = context.globalData.verifications;
284
+
285
+ const profile = context.math.VerificationExtractor.getProfile(verifications, userId);
286
+ if (profile) {
287
+ const isVerified = context.math.VerificationExtractor.isVerified(profile);
288
+ const realCid = context.math.VerificationExtractor.getRealCID(profile);
289
+ const username = context.math.VerificationExtractor.getUsername(profile);
290
+ const bio = context.math.VerificationExtractor.getAboutMe(profile);
291
+ const restrictions = context.math.VerificationExtractor.getRestrictions(profile);
292
+
293
+ // Check specific restriction
294
+ const hasRestriction = context.math.VerificationExtractor.hasRestriction(profile, 5);
295
+ }
296
+ ```
297
+
298
+ #### Social Data Extraction
299
+
300
+ ```javascript
301
+ const discussions = context.math.SocialExtractor.getDiscussions(context.social.today);
302
+
303
+ discussions.forEach(disc => {
304
+ const postId = context.math.SocialExtractor.getPostId(disc);
305
+ const message = context.math.SocialExtractor.getMessage(disc);
306
+ const language = context.math.SocialExtractor.getLanguage(disc);
307
+ const created = context.math.SocialExtractor.getCreatedDate(disc); // Date object
308
+ const owner = context.math.SocialExtractor.getOwnerUsername(disc);
309
+ const isVerified = context.math.SocialExtractor.isOwnerVerified(disc);
310
+ const contextType = context.math.SocialExtractor.getPostContextType(disc);
311
+ // 'INSTRUMENT_TAGGED' or 'USER_TIMELINE'
312
+
313
+ const instruments = context.math.SocialExtractor.getRelatedInstruments(disc);
314
+ // Returns: [{ id: 1, symbol: 'BTC', displayName: 'Bitcoin' }, ...]
315
+
316
+ const likes = context.math.SocialExtractor.getLikeCount(disc);
317
+ const comments = context.math.SocialExtractor.getCommentCount(disc);
318
+ });
319
+ ```
320
+
321
+ #### Profile Metrics (Ratings, Page Views, Watchlist, Alerts)
322
+
323
+ ```javascript
324
+ // PI Ratings
325
+ const ratings = context.globalData.ratings;
326
+ const avgRating = context.math.RatingsExtractor.getAverageRating(ratings, piCid);
327
+ const totalRatings = context.math.RatingsExtractor.getTotalRatings(ratings, piCid);
328
+ const userRating = context.math.RatingsExtractor.getUserRating(ratings, piCid, userId);
329
+
330
+ // Page Views
331
+ const pageViews = context.globalData.pageViews;
332
+ const totalViews = context.math.PageViewsExtractor.getTotalViews(pageViews, piCid);
333
+ const uniqueViewers = context.math.PageViewsExtractor.getUniqueViewers(pageViews, piCid);
334
+ const userViews = context.math.PageViewsExtractor.getUserViewCount(pageViews, piCid, userId);
335
+ const lastViewed = context.math.PageViewsExtractor.getUserLastViewed(pageViews, piCid, userId);
336
+
337
+ // Watchlist Membership
338
+ const watchlist = context.globalData.watchlistMembership;
339
+ const totalWatchers = context.math.WatchlistMembershipExtractor.getTotalUsers(watchlist, piCid);
340
+ const isWatched = context.math.WatchlistMembershipExtractor.hasUser(watchlist, piCid, userId);
341
+ const publicCount = context.math.WatchlistMembershipExtractor.getPublicWatchlistCount(watchlist, piCid);
342
+
343
+ // Alert History
344
+ const alerts = context.globalData.alertHistory;
345
+ const alertTypes = context.math.AlertHistoryExtractor.getAlertTypes(alerts, piCid);
346
+ const isTriggered = context.math.AlertHistoryExtractor.isTriggered(alerts, piCid, 'RiskScoreIncrease');
347
+ const triggeredFor = context.math.AlertHistoryExtractor.getTriggeredFor(alerts, piCid, 'RiskScoreIncrease');
348
+ // Returns: [userId1, userId2, ...]
349
+ ```
350
+
351
+ #### Price Data Extraction
352
+
353
+ ```javascript
354
+ const priceHistory = context.math.priceExtractor.getHistory(
355
+ context.prices,
356
+ 'AAPL' // or instrumentId
357
+ );
358
+ // Returns: [{ date: '2024-01-15', price: 150.25 }, ...]
359
+ ```
360
+
361
+ ---
362
+
363
+ ### Mathematical Primitives (`context.math.compute` / `context.math.MathPrimitives`)
364
+
365
+ **Purpose:** Core statistical and mathematical operations.
366
+
367
+ ```javascript
368
+ // Basic Statistics
369
+ const avg = context.math.compute.average([10, 20, 30]); // 20
370
+ const med = context.math.compute.median([1, 5, 10, 15, 20]); // 10
371
+ const stdDev = context.math.compute.standardDeviation([10, 20, 30, 40]);
372
+
373
+ // Financial Probability
374
+ const hitProb = context.math.compute.calculateHitProbability(
375
+ currentPrice, // Current asset price
376
+ barrierPrice, // Target/barrier price
377
+ volatility, // Annualized volatility (0.3 = 30%)
378
+ days, // Time horizon
379
+ drift // Expected return (0.05 = 5% annual)
380
+ );
381
+ // Returns: Probability (0-1) of hitting barrier
382
+
383
+ // Monte Carlo Simulation
384
+ const pricePaths = context.math.compute.simulateGBM(
385
+ currentPrice,
386
+ volatility,
387
+ days,
388
+ simulations, // Number of paths (e.g., 1000)
389
+ drift
390
+ );
391
+ // Returns: Float32Array of simulated prices
392
+
393
+ // Population Breakdown Simulation
394
+ const breakdownProb = context.math.compute.simulatePopulationBreakdown(
395
+ pricePaths,
396
+ userProfiles // [{ entryPrice: 100, thresholdPct: -10 }, ...]
397
+ );
398
+ // Returns: Average fraction of users who capitulate
399
+ ```
400
+
401
+ ---
402
+
403
+ ### Financial Engineering (`context.math.FinancialEngineering`)
404
+
405
+ **Purpose:** Advanced risk metrics.
406
+
407
+ ```javascript
408
+ // Sortino Ratio (downside-focused Sharpe)
409
+ const sortino = context.math.FinancialEngineering.sortinoRatio(
410
+ returnSeries, // [0.05, -0.02, 0.03, ...]
411
+ targetReturn // 0.0 for zero benchmark
412
+ );
413
+
414
+ // Kelly Criterion (optimal position sizing)
415
+ const kellyFraction = context.math.FinancialEngineering.kellyCriterion(
416
+ winRatio, // 60 (percent)
417
+ avgWinPct, // 5 (percent)
418
+ avgLossPct // 3 (percent)
419
+ );
420
+ // Returns: Optimal capital allocation (0.2 = 20% of capital)
421
+ ```
422
+
423
+ ---
424
+
425
+ ### Time Series Analysis (`context.math.TimeSeries` / `context.math.TimeSeriesAnalysis`)
426
+
427
+ **Purpose:** Pattern detection and forecasting.
428
+
429
+ ```javascript
430
+ // Exponential Moving Average (state-based)
431
+ let emaState = null;
432
+ positions.forEach(pos => {
433
+ const pnl = context.math.extract.getNetProfit(pos);
434
+ emaState = context.math.TimeSeries.updateEMAState(pnl, emaState, 0.1);
435
+ });
436
+ const smoothedPnl = emaState.mean;
437
+
438
+ // Correlation Analysis
439
+ const correlation = context.math.TimeSeries.pearsonCorrelation(
440
+ assetReturns1,
441
+ assetReturns2
442
+ );
443
+ // Returns: -1 to 1
444
+
445
+ // Sliding Window Min/Max (O(n) algorithm)
446
+ const { min, max } = context.math.TimeSeries.slidingWindowExtrema(
447
+ priceSeries,
448
+ windowSize // 20 for 20-day window
449
+ );
450
+ // Returns: { min: [minValues...], max: [maxValues...] }
451
+
452
+ // Hurst Exponent (trend detection)
453
+ const hurst = context.math.TimeSeriesAnalysis.hurstExponent(priceSeries);
454
+ // Returns: 0-1 (0.5 = random walk, >0.5 = trending, <0.5 = mean-reverting)
455
+
456
+ // Fast Fourier Transform (cycle detection)
457
+ const fftResult = context.math.TimeSeriesAnalysis.fft(priceSeries);
458
+ // Returns: [{ real: x, imag: y }, ...]
459
+ ```
460
+
461
+ ---
462
+
463
+ ### Signal Primitives (`context.math.signals` / `context.math.SignalPrimitives`)
464
+
465
+ **Purpose:** Access other computation results and transformations.
466
+
467
+ ```javascript
468
+ // Get metric from dependency
469
+ const riskScore = context.math.signals.getMetric(
470
+ context.computed, // Dependency results
471
+ 'portfolio-risk', // Calculation name
472
+ 'AAPL', // Ticker or user ID
473
+ 'score', // Field name
474
+ 0 // Fallback if missing
475
+ );
476
+
477
+ // Get all tickers/users from multiple dependencies
478
+ const allTickers = context.math.signals.getUnionKeys(
479
+ context.computed,
480
+ ['momentum-score', 'volatility-index']
481
+ );
482
+
483
+ // Normalization
484
+ const normalized = context.math.signals.normalizeTanh(rawValue, 10, 10.0);
485
+ // Scales to [-10, 10] with tanh smoothing
486
+
487
+ const zScore = context.math.signals.normalizeZScore(value, mean, stdDev);
488
+
489
+ // Divergence detection
490
+ const divergence = context.math.signals.divergence(
491
+ valueA, // Current metric
492
+ valueB // Benchmark metric
493
+ );
494
+
495
+ // Access previous computation state
496
+ const prevScore = context.math.signals.getPreviousState(
497
+ context.previousComputed,
498
+ 'risk-score',
499
+ 'AAPL',
500
+ 'value'
501
+ );
502
+ ```
503
+
504
+ ---
505
+
506
+ ### Aggregators (`context.math.aggregate` / `context.math.Aggregators`)
507
+
508
+ **Purpose:** Multi-user analysis (mainly for Meta computations).
509
+
510
+ ```javascript
511
+ // Group users by PnL per asset
512
+ const buckets = context.math.aggregate.bucketUsersByPnlPerAsset(
513
+ context.computed['portfolio-snapshot'], // All user portfolios
514
+ context.mappings.instrumentToTicker
515
+ );
516
+ // Returns: {
517
+ // 'AAPL': { winners: ['user1', 'user2'], losers: ['user3'] },
518
+ // 'BTC': { winners: [...], losers: [...] }
519
+ // }
520
+
521
+ // Weighted sentiment calculation
522
+ const sentiment = context.math.aggregate.getWeightedSentiment(positions);
523
+ // Returns: Weighted average PnL
524
+ ```
525
+
526
+ ---
527
+
528
+ ### Distribution Analytics (`context.math.distribution` / `context.math.DistributionAnalytics`)
529
+
530
+ **Purpose:** Kernel density estimation and statistical modeling.
531
+
532
+ ```javascript
533
+ // Kernel Density Estimation
534
+ const profile = context.math.distribution.computeKDE(
535
+ dataPoints, // [{ value: 100, weight: 1.5 }, ...]
536
+ bandwidth, // 5.0 (smoothing parameter)
537
+ steps // 60 (resolution)
538
+ );
539
+ // Returns: [{ price: x, density: y }, ...]
540
+
541
+ // Integration (area under curve)
542
+ const probability = context.math.distribution.integrateProfile(
543
+ densityCurve,
544
+ startPrice,
545
+ endPrice
546
+ );
547
+
548
+ // Linear Regression
549
+ const { slope, n } = context.math.distribution.linearRegression(
550
+ xValues,
551
+ yValues
552
+ );
553
+ ```
554
+
555
+ ---
556
+
557
+ ### Linear Algebra (`context.math.LinearAlgebra`)
558
+
559
+ **Purpose:** Advanced anomaly detection and correlation analysis.
560
+
561
+ ```javascript
562
+ // Covariance Matrix
563
+ const { matrix, means } = context.math.LinearAlgebra.covarianceMatrix(
564
+ dataMatrix // [[feature1, feature2, ...], [f1, f2, ...], ...]
565
+ );
566
+
567
+ // Matrix Inversion (for Mahalanobis distance)
568
+ const inverse = context.math.LinearAlgebra.invertMatrix(matrix);
569
+
570
+ // Mahalanobis Distance (outlier detection)
571
+ const distance = context.math.LinearAlgebra.mahalanobisDistance(
572
+ currentVector, // [userRisk, leverage, volatility]
573
+ means, // Baseline mean vector
574
+ inverseCovariance // Inverted covariance matrix
575
+ );
576
+ // Returns: Standard deviations from baseline
577
+ ```
578
+
579
+ ---
580
+
581
+ ### Profiling & Classification (`context.math`)
582
+
583
+ **Purpose:** User intelligence scoring and behavioral analysis.
584
+
585
+ #### User Classification
586
+
587
+ ```javascript
588
+ const classification = context.math.classifier.classify(context);
589
+ // Returns: {
590
+ // intelligence: { label: 'Smart Money', score: 75, isSmart: true },
591
+ // style: { primary: 'Swing Trader' },
592
+ // metrics: { profitFactor: 1.8, allocEfficiency: 0.6 }
593
+ // }
594
+ ```
595
+
596
+ #### Smart Money Scoring
597
+
598
+ ```javascript
599
+ // Score portfolio quality
600
+ const portfolioScore = context.math.SmartMoneyScorer.scorePortfolio(
601
+ context.user.portfolio.today,
602
+ context.user.type,
603
+ context.prices,
604
+ context.mappings,
605
+ context.math
606
+ );
607
+ // Returns: { score: 0-100, metrics: {...} }
608
+
609
+ // Score trading history
610
+ const historyScore = context.math.SmartMoneyScorer.scoreHistory(
611
+ context.user.history.today,
612
+ context.prices,
613
+ context.mappings,
614
+ context.math
615
+ );
616
+ // Returns: { score: 0-100, metrics: {...} }
617
+
618
+ // Hybrid analysis
619
+ const hybridScore = context.math.SmartMoneyScorer.scoreHybrid(context);
620
+ // Returns: {
621
+ // totalScore: 75,
622
+ // label: 'Smart Money',
623
+ // method: 'Hybrid',
624
+ // components: { portfolio: {...}, history: {...} }
625
+ // }
626
+ ```
627
+
628
+ #### Cognitive Biases Detection
629
+
630
+ ```javascript
631
+ // Anchoring Bias (holding losing positions too long)
632
+ const anchoringScore = context.math.bias.calculateAnchoringScore(
633
+ positions,
634
+ thresholdPct, // 2.0 = positions within ±2% of entry
635
+ minDaysHeld // 14 days
636
+ );
637
+ // Returns: 0-1 (fraction of anchored positions)
638
+
639
+ // Disposition Effect (selling winners too early, holding losers)
640
+ const dispositionRatio = context.math.bias.calculateDispositionEffect(
641
+ trades
642
+ );
643
+ // Returns: Ratio (>1 = holding losers longer than winners)
644
+ ```
645
+
646
+ #### Skill Attribution
647
+
648
+ ```javascript
649
+ // Selection Alpha (stock-picking skill vs market)
650
+ const alpha = context.math.skill.calculateSelectionAlpha(
651
+ positions,
652
+ dailyInsights // Market consensus data
653
+ );
654
+ // Returns: Average excess return
655
+ ```
656
+
657
+ #### Execution Quality
658
+
659
+ ```javascript
660
+ // Entry timing efficiency
661
+ const efficiency = context.math.execution.calculateEfficiency(
662
+ entryPrice,
663
+ priceHistory,
664
+ entryDate,
665
+ direction, // 'Buy' or 'Sell'
666
+ windowDays // 7 (compare to ±7 day range)
667
+ );
668
+ // Returns: 0-1 (1 = perfect entry at extreme)
669
+
670
+ // Loss tolerance
671
+ const tolerance = context.math.execution.calculateLossTolerance(
672
+ realizedPnL,
673
+ maxDrawdown
674
+ );
675
+ ```
676
+
677
+ #### Psychometrics
678
+
679
+ ```javascript
680
+ // Disposition Skew (realized vs unrealized PnL)
681
+ const skew = context.math.psychometrics.computeDispositionSkew(
682
+ trades,
683
+ currentPositions
684
+ );
685
+
686
+ // Revenge Trading Detection
687
+ const revengeScore = context.math.psychometrics.detectRevengeTrading(
688
+ trades // Sorted chronologically
689
+ );
690
+ // Returns: 0-1 (fraction of losses followed by risk escalation)
691
+ ```
692
+
693
+ #### Adaptive Analytics
694
+
695
+ ```javascript
696
+ // Drawdown Adaptation (does user learn from losses?)
697
+ const adaptationScore = context.math.adaptive.analyzeDrawdownAdaptation(
698
+ trades,
699
+ drawdownThreshold // -15 (percent)
700
+ );
701
+ // Returns: Score (-2 to +1, positive = adaptive behavior)
702
+ ```
703
+
704
+ #### Risk Geometry
705
+
706
+ ```javascript
707
+ // Efficient Frontier (Convex Hull)
708
+ const efficientFrontier = context.math.RiskGeometry.computeConvexHull(
709
+ portfolioPoints // [{ x: risk, y: return }, ...]
710
+ );
711
+ // Returns: Points on the efficient frontier
712
+ ```
713
+
714
+ #### Anomaly Detection
715
+
716
+ ```javascript
717
+ const anomalies = context.math.AnomalyDetector.detect(
718
+ trades,
719
+ currentPositions,
720
+ context.mappings
721
+ );
722
+ // Returns: {
723
+ // anomalies: ['New Sector Entry: User entered Technology sector for first time'],
724
+ // warnings: ['High Leverage Alert: Position 100 has 10x leverage (Avg: 2.5x)']
725
+ // }
726
+ ```
727
+
728
+ #### Similarity Engine
729
+
730
+ ```javascript
731
+ // Find similar traders
732
+ const peers = context.math.SimilarityEngine.findPeers(
733
+ context, // Current user context
734
+ candidateContexts, // Array of other users
735
+ limit // 5 (top matches)
736
+ );
737
+ // Returns: [
738
+ // { userId: '123', score: 0.85, matches: ['AAPL', 'GOOGL', 'MSFT'] },
739
+ // ...
740
+ // ]
741
+ ```
742
+
743
+ ---
744
+
745
+ ### Trade Series Builder (`context.math.TradeSeriesBuilder`)
746
+
747
+ **Purpose:** Convert trade history to time series for analysis.
748
+
749
+ ```javascript
750
+ // Build return series
751
+ const returns = context.math.TradeSeriesBuilder.buildReturnSeries(trades);
752
+ // Returns: [netProfit1, netProfit2, ...] chronologically
753
+
754
+ // Build cumulative equity curve
755
+ const curve = context.math.TradeSeriesBuilder.buildCumulativeCurve(
756
+ returns,
757
+ startValue // 100 (starting capital)
758
+ );
759
+ // Returns: [100, 110, 105, 120, ...]
760
+
761
+ // Calculate max drawdown
762
+ const { maxDrawdownPct, peakIndex, troughIndex } =
763
+ context.math.TradeSeriesBuilder.calculateMaxDrawdown(curve);
764
+ ```
765
+
766
+ ---
767
+
768
+ ### Validators (`context.math.validate` / `context.math.Validators`)
769
+
770
+ **Purpose:** Validate data integrity (mainly for internal use, but available).
771
+
772
+ ```javascript
773
+ const portfolioCheck = context.math.validate.validatePortfolio(
774
+ context.user.portfolio.today,
775
+ context.user.type
776
+ );
777
+ // Returns: { valid: boolean, errors: string[] }
778
+
779
+ const historyCheck = context.math.validate.validateTradeHistory(
780
+ context.user.history.today
781
+ );
782
+
783
+ const socialCheck = context.math.validate.validateSocialPost(post);
784
+
785
+ const insightCheck = context.math.validate.validateInsight(insight);
786
+
787
+ const priceCheck = context.math.validate.validatePriceData(instrumentData);
788
+ ```
789
+
790
+ ---
791
+
792
+ ### Schemas (`context.math.schemas` / `context.math.SCHEMAS`)
793
+
794
+ **Purpose:** Constants and schema definitions.
795
+
796
+ ```javascript
797
+ // User Type Constants
798
+ context.math.SCHEMAS.USER_TYPES.NORMAL // 'normal'
799
+ context.math.SCHEMAS.USER_TYPES.SPECULATOR // 'speculator'
800
+ context.math.SCHEMAS.USER_TYPES.SIGNED_IN // 'SIGNED_IN_USER'
801
+ context.math.SCHEMAS.USER_TYPES.POPULAR // 'POPULAR_INVESTOR'
802
+
803
+ // Trading Style Labels
804
+ context.math.SCHEMAS.STYLES.INVESTOR // 'Investor'
805
+ context.math.SCHEMAS.STYLES.SWING_TRADER // 'Swing Trader'
806
+ context.math.SCHEMAS.STYLES.DAY_TRADER // 'Day Trader'
807
+ context.math.SCHEMAS.STYLES.SCALPER // 'Scalper'
808
+
809
+ // Intelligence Labels
810
+ context.math.SCHEMAS.LABELS.ELITE // 'Elite'
811
+ context.math.SCHEMAS.LABELS.SMART // 'Smart Money'
812
+ context.math.SCHEMAS.LABELS.NEUTRAL // 'Neutral'
813
+ context.math.SCHEMAS.LABELS.DUMB // 'Dumb Money'
814
+ context.math.SCHEMAS.LABELS.GAMBLER // 'Gambler'
815
+ ```
816
+
817
+ ---
818
+
819
+ ## Complete Examples
820
+
821
+ ### Example 1: Simple User Profiler (Standard)
822
+
823
+ ```javascript
824
+ class UserRiskProfile
825
+ {
826
+ static getMetadata() {
827
+ return {
828
+ type: 'standard',
829
+ category: 'profiling',
830
+ rootDataDependencies: ['portfolio', 'history'],
831
+ userType: 'all'
832
+ };
833
+ }
834
+
835
+ static getDependencies() {
836
+ return [];
837
+ }
838
+
839
+ static getSchema() {
840
+ return {
841
+ type: 'object',
842
+ patternProperties: {
843
+ '^[0-9]+$': {
844
+ type: 'object',
845
+ properties: {
846
+ riskScore: { type: 'number' },
847
+ category: { type: 'string' }
848
+ }
849
+ }
850
+ }
851
+ };
852
+ }
853
+
854
+ async process(context) {
855
+ const positions = context.math.extract.getPositions(
856
+ context.user.portfolio.today,
857
+ context.user.type
858
+ );
859
+
860
+ const leverage = positions.reduce((sum, p) =>
861
+ sum + context.math.extract.getLeverage(p), 0
862
+ ) / positions.length;
863
+
864
+ this.results = {
865
+ [context.user.id]: {
866
+ riskScore: Math.min(100, leverage * 20),
867
+ category: leverage > 3 ? 'Aggressive' : 'Conservative'
868
+ }
869
+ };
870
+ }
871
+
872
+ async getResult() {
873
+ return this.results;
874
+ }
875
+ }
876
+ ```
877
+
878
+ ### Example 2: Advanced Statistical Profiler
879
+
880
+ ```javascript
881
+ class AdvancedTradingProfile {
882
+ static getMetadata() {
883
+ return {
884
+ type: 'standard',
885
+ category: 'profiling',
886
+ rootDataDependencies: ['portfolio', 'history', 'insights'],
887
+ userType: 'all'
888
+ };
889
+ }
890
+
891
+ static getDependencies() {
892
+ return [];
893
+ }
894
+
895
+ static getSchema() {
896
+ return {
897
+ type: 'object',
898
+ patternProperties: {
899
+ '^[0-9]+$': {
900
+ type: 'object',
901
+ properties: {
902
+ intelligence: { type: 'object' },
903
+ biases: { type: 'object' },
904
+ executionQuality: { type: 'number' },
905
+ riskMetrics: { type: 'object' }
906
+ }
907
+ }
908
+ }
909
+ };
910
+ }
911
+
912
+ async process(context) {
913
+ // Use pre-built classifier
914
+ const classification = context.math.classifier.classify(context);
915
+
916
+ // Detect cognitive biases
917
+ const positions = context.math.extract.getPositions(
918
+ context.user.portfolio.today,
919
+ context.user.type
920
+ );
921
+ const trades = context.math.history.getTrades(context.user.history.today);
922
+
923
+ const anchoring = context.math.bias.calculateAnchoringScore(positions);
924
+ const disposition = context.math.bias.calculateDispositionEffect(trades);
925
+ const revenge = context.math.psychometrics.detectRevengeTrading(trades);
926
+
927
+ // Calculate trade series metrics
928
+ const returns = context.math.TradeSeriesBuilder.buildReturnSeries(trades);
929
+ const curve = context.math.TradeSeriesBuilder.buildCumulativeCurve(returns);
930
+ const { maxDrawdownPct } = context.math.TradeSeriesBuilder.calculateMaxDrawdown(curve);
931
+
932
+ // Sortino ratio
933
+ const sortino = context.math.FinancialEngineering.sortinoRatio(returns);
934
+
935
+ this.results = {
936
+ [context.user.id]: {
937
+ intelligence: classification.intelligence,
938
+ biases: {
939
+ anchoring: anchoring,
940
+ disposition: disposition,
941
+ revenge: revenge
942
+ },
943
+ executionQuality: classification.metrics.allocEfficiency,
944
+ riskMetrics: {
945
+ maxDrawdown: maxDrawdownPct,
946
+ sortino: sortino
947
+ }
948
+ }
949
+ };
950
+ }
951
+
952
+ async getResult() {
953
+ return this.results;
954
+ }
955
+ }
956
+ ```
957
+
958
+ ### Example 3: Market Sentiment Aggregator (Meta)
959
+
960
+ ```javascript
961
+ class MarketSentimentIndex {
962
+ static getMetadata() {
963
+ return {
964
+ type: 'meta',
965
+ category: 'analytics',
966
+ rootDataDependencies: ['insights', 'rankings']
967
+ };
968
+ }
969
+
970
+ static getDependencies() {
971
+ return ['advanced-trading-profile'];
972
+ }
973
+
974
+ static getSchema() {
975
+ return {
976
+ type: 'object',
977
+ properties: {
978
+ fearGreedIndex: { type: 'number' },
979
+ topSectors: { type: 'array' },
980
+ smartMoneyFlow: { type: 'object' }
981
+ }
982
+ };
983
+ }
984
+
985
+ async process(context) {
986
+ const insights = context.math.insights.getInsights(context, 'today');
987
+
988
+ // Aggregate market sentiment
989
+ let totalBullish = 0;
990
+ let totalBearish = 0;
991
+
992
+ insights.forEach(insight => {
993
+ const long = context.math.insights.getLongPercent(insight);
994
+ const short = context.math.insights.getShortPercent(insight);
995
+ totalBullish += long;
996
+ totalBearish += short;
997
+ });
998
+
999
+ const fearGreed = (totalBullish / (totalBullish + totalBearish)) * 100;
1000
+
1001
+ // Get smart money positions from dependency
1002
+ const profiles = context.computed['advanced-trading-profile'];
1003
+ const smartMoney = Object.entries(profiles)
1004
+ .filter(([_, p]) => p.intelligence.isSmart)
1005
+ .map(([userId, _]) => userId);
1006
+
1007
+ // Analyze rankings
1008
+ const rankings = context.globalData.rankings;
1009
+ const topPIs = rankings.slice(0, 10);
1010
+ const topSectors = topPIs.map(pi => {
1011
+ const tags = context.math.RankingsExtractor.getTags(pi);
1012
+ return tags[0]; // First tag
1013
+ }).filter(Boolean);
1014
+
1015
+ this.results = {
1016
+ fearGreedIndex: fearGreed,
1017
+ topSectors: [...new Set(topSectors)],
1018
+ smartMoneyFlow: {
1019
+ count: smartMoney.length,
1020
+ percentage: (smartMoney.length / Object.keys(profiles).length) * 100
1021
+ }
1022
+ };
1023
+ }
1024
+
1025
+ async getResult() {
1026
+ return this.results;
1027
+ }
1028
+ }
1029
+ ```
1030
+
1031
+ ### Example 4: Time Series Momentum with Lookback
1032
+
1033
+ ```javascript
1034
+ class MomentumIndicator {
1035
+ static getMetadata() {
1036
+ return {
1037
+ type: 'standard',
1038
+ category: 'signals',
1039
+ rootDataDependencies: ['portfolio'],
1040
+ rootDataSeries: {
1041
+ insights: 30 // 30 days of market data
1042
+ },
1043
+ dependencySeries: {
1044
+ 'user-risk-profiler': 7 // 7 days of risk scores
1045
+ }
1046
+ };
1047
+ }
1048
+
1049
+ static getDependencies() {
1050
+ return ['user-risk-profiler'];
1051
+ }
1052
+
1053
+ static getSchema() {
1054
+ return {
1055
+ type: 'object',
1056
+ patternProperties: {
1057
+ '^[0-9]+$': {
1058
+ type: 'object',
1059
+ properties: {
1060
+ momentum: { type: 'number' },
1061
+ volatility: { type: 'number' },
1062
+ trend: { type: 'string' }
1063
+ }
1064
+ }
1065
+ }
1066
+ };
1067
+ }
1068
+
1069
+ async process(context) {
1070
+ // Build risk score time series
1071
+ const riskHistory = [];
1072
+ const seriesResults = context.globalData.series.results;
1073
+
1074
+ for (const [date, dateResults] of Object.entries(seriesResults)) {
1075
+ const userScore = dateResults['user-risk-profiler']?.[context.user.id];
1076
+ if (userScore) {
1077
+ riskHistory.push(userScore.riskScore);
1078
+ }
1079
+ }
1080
+
1081
+ if (riskHistory.length < 3) {
1082
+ this.results = {
1083
+ [context.user.id]: { momentum: 0, volatility: 0, trend: 'Insufficient Data' }
1084
+ };
1085
+ return;
1086
+ }
1087
+
1088
+ // Calculate momentum (recent avg vs older avg)
1089
+ const recent = riskHistory.slice(-3);
1090
+ const older = riskHistory.slice(0, 3);
1091
+ const momentum = context.math.compute.average(recent) - context.math.compute.average(older);
1092
+
1093
+ // Calculate volatility
1094
+ const volatility = context.math.compute.standardDeviation(riskHistory);
1095
+
1096
+ // Detect trend with Hurst exponent
1097
+ const hurst = context.math.TimeSeriesAnalysis.hurstExponent(riskHistory);
1098
+ const trend = hurst > 0.55 ? 'Trending' : (hurst < 0.45 ? 'Mean Reverting' : 'Random Walk');
1099
+
1100
+ this.results = {
1101
+ [context.user.id]: {
1102
+ momentum: momentum,
1103
+ volatility: volatility,
1104
+ trend: trend
1105
+ }
1106
+ };
1107
+ }
1108
+
1109
+ async getResult() {
1110
+ return this.results;
1111
+ }
1112
+ }
1113
+ ```
1114
+
1115
+ ---
1116
+
1117
+ ## Key Decisions
1118
+
1119
+ **Choose `type: 'standard'` if:**
1120
+ - You analyze individual users (profiling, scoring, alerts)
1121
+ - Output is per-user (keyed by user ID)
1122
+
1123
+ **Choose `type: 'meta'` if:**
1124
+ - You aggregate across all users (leaderboards, market stats)
1125
+ - Output is global (single object or keyed by asset)
1126
+
1127
+ **Use `isHistorical: true` if:**
1128
+ - You need yesterday's result to calculate today's (streaks, deltas, cumulative)
1129
+
1130
+ **Use `schedule` if:**
1131
+ - Computation is expensive and daily runs are wasteful (weekly reports, monthly summaries)
1132
+
1133
+ **Use `isPage: true` if:**
1134
+ - Output is large (>10K users) and needs subcollection storage
1135
+
1136
+ **Use `ttlDays` if:**
1137
+ - Data has a natural expiration (ephemeral alerts: 30 days, annual reports: 365 days)
1138
+
1139
+ ---
1140
+
1141
+ ## Common Patterns
1142
+
1143
+ ### Pattern: Multi-Layer Analysis
1144
+ ```javascript
1145
+ // Combine multiple extractors
1146
+ const positions = context.math.extract.getPositions(...);
1147
+ const trades = context.math.history.getTrades(...);
1148
+ const summary = context.math.history.getSummary(...);
1149
+ const classification = context.math.classifier.classify(context);
1150
+
1151
+ // Build composite score
1152
+ const score = (summary.winRatio * 0.4) + (classification.intelligence.score * 0.6);
1153
+ ```
1154
+
1155
+ ### Pattern: Statistical Outlier Detection
1156
+ ```javascript
1157
+ // Use Mahalanobis distance
1158
+ const features = [userRisk, userLeverage, userVolatility];
1159
+ const { matrix, means } = context.math.LinearAlgebra.covarianceMatrix(allUserFeatures);
1160
+ const inverse = context.math.LinearAlgebra.invertMatrix(matrix);
1161
+ const distance = context.math.LinearAlgebra.mahalanobisDistance(features, means, inverse);
1162
+
1163
+ const isOutlier = distance > 3; // 3 standard deviations
1164
+ ```
1165
+
1166
+ ### Pattern: Behavioral State Machine
1167
+ ```javascript
1168
+ const prevState = context.previousComputed['behavior-tracker']?.[userId]?.state || 'neutral';
1169
+ const currentRisk = context.computed['risk-score'][userId];
1170
+
1171
+ let newState = prevState;
1172
+ if (currentRisk > 80 && prevState !== 'aggressive') {
1173
+ newState = 'aggressive';
1174
+ } else if (currentRisk < 30 && prevState !== 'conservative') {
1175
+ newState = 'conservative';
1176
+ }
1177
+ ```
1178
+
1179
+ ---
1180
+
1181
+ ## Testing Your Computation
1182
+
1183
+ 1. **Implement the three static methods**
1184
+ 2. **Test Schema:** Run `YourClass.getSchema()` - ensure it returns valid JSON Schema
1185
+ 3. **Test Metadata:** Check dependencies exist in manifest
1186
+ 4. **Mark as Test:** Set `isTest: true` during development for immediate re-runs
1187
+ 5. **Check Logs:** Monitor Cloud Logging for validation errors
1188
+ 6. **Verify Math Layer:** Test helper functions locally before deploying
1189
+
1190
+ ---
1191
+
1192
+ ## What Happens When You Submit
1193
+
1194
+ 1. **Manifest Build:** System discovers your class, validates static methods
1195
+ 2. **Dependency Graph:** Ensures dependencies exist, detects cycles
1196
+ 3. **Hash Generation:** Code changes trigger automatic re-runs
1197
+ 4. **Execution:** System loads data, injects math layer, calls `process(context)`, validates output
1198
+ 5. **Storage:** Results compressed/sharded automatically based on size
1199
+
1200
+ You control the logic. The system handles infrastructure, retries, validation, and optimization.