aiden-shared-calculations-unified 1.0.146 → 1.0.147
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/calculations/creatingcomputations.md +1200 -0
- package/calculations/popular-investor/AggregateAssetAUM.js +1 -1
- package/calculations/popular-investor/AggregateDailyAUM.js +1 -1
- package/calculations/popular-investor/AggregateSectorRanking.js +1 -1
- package/calculations/popular-investor/AggregateStockRanking.js +1 -1
- package/calculations/popular-investor/AumLeaderboard.js +68 -0
- package/calculations/popular-investor/GlobalAumPerAsset30D.js +42 -0
- package/calculations/popular-investor/PIDailyAssetAUM.js +101 -0
- package/calculations/popular-investor/PiAssetRecommender.js +1 -1
- package/calculations/popular-investor/PiSimilarityMatrix.js +5 -7
- package/calculations/popular-investor/RecommendedPopularInvestors.js +89 -176
- package/calculations/popular-investor/RiskLeaderboard.js +64 -0
- package/package.json +1 -1
|
@@ -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.
|