bulltrackers-module 1.0.732 → 1.0.734

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 (106) hide show
  1. package/functions/computation-system-v2/README.md +152 -0
  2. package/functions/computation-system-v2/computations/PopularInvestorProfileMetrics.js +720 -0
  3. package/functions/computation-system-v2/computations/PopularInvestorRiskAssessment.js +176 -0
  4. package/functions/computation-system-v2/computations/PopularInvestorRiskMetrics.js +294 -0
  5. package/functions/computation-system-v2/computations/TestComputation.js +46 -0
  6. package/functions/computation-system-v2/computations/UserPortfolioSummary.js +172 -0
  7. package/functions/computation-system-v2/config/bulltrackers.config.js +317 -0
  8. package/functions/computation-system-v2/framework/core/Computation.js +73 -0
  9. package/functions/computation-system-v2/framework/core/Manifest.js +223 -0
  10. package/functions/computation-system-v2/framework/core/RuleInjector.js +53 -0
  11. package/functions/computation-system-v2/framework/core/Rules.js +231 -0
  12. package/functions/computation-system-v2/framework/core/RunAnalyzer.js +163 -0
  13. package/functions/computation-system-v2/framework/cost/CostTracker.js +154 -0
  14. package/functions/computation-system-v2/framework/data/DataFetcher.js +399 -0
  15. package/functions/computation-system-v2/framework/data/QueryBuilder.js +232 -0
  16. package/functions/computation-system-v2/framework/data/SchemaRegistry.js +287 -0
  17. package/functions/computation-system-v2/framework/execution/Orchestrator.js +498 -0
  18. package/functions/computation-system-v2/framework/execution/TaskRunner.js +35 -0
  19. package/functions/computation-system-v2/framework/execution/middleware/CostTrackerMiddleware.js +32 -0
  20. package/functions/computation-system-v2/framework/execution/middleware/LineageMiddleware.js +32 -0
  21. package/functions/computation-system-v2/framework/execution/middleware/Middleware.js +14 -0
  22. package/functions/computation-system-v2/framework/execution/middleware/ProfilerMiddleware.js +47 -0
  23. package/functions/computation-system-v2/framework/index.js +45 -0
  24. package/functions/computation-system-v2/framework/lineage/LineageTracker.js +147 -0
  25. package/functions/computation-system-v2/framework/monitoring/Profiler.js +80 -0
  26. package/functions/computation-system-v2/framework/resilience/Checkpointer.js +66 -0
  27. package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +327 -0
  28. package/functions/computation-system-v2/framework/storage/StateRepository.js +286 -0
  29. package/functions/computation-system-v2/framework/storage/StorageManager.js +469 -0
  30. package/functions/computation-system-v2/framework/storage/index.js +9 -0
  31. package/functions/computation-system-v2/framework/testing/ComputationTester.js +86 -0
  32. package/functions/computation-system-v2/framework/utils/Graph.js +205 -0
  33. package/functions/computation-system-v2/handlers/dispatcher.js +109 -0
  34. package/functions/computation-system-v2/handlers/index.js +23 -0
  35. package/functions/computation-system-v2/handlers/onDemand.js +289 -0
  36. package/functions/computation-system-v2/handlers/scheduler.js +327 -0
  37. package/functions/computation-system-v2/index.js +163 -0
  38. package/functions/computation-system-v2/rules/index.js +49 -0
  39. package/functions/computation-system-v2/rules/instruments.js +465 -0
  40. package/functions/computation-system-v2/rules/metrics.js +304 -0
  41. package/functions/computation-system-v2/rules/portfolio.js +534 -0
  42. package/functions/computation-system-v2/rules/rankings.js +655 -0
  43. package/functions/computation-system-v2/rules/social.js +562 -0
  44. package/functions/computation-system-v2/rules/trades.js +545 -0
  45. package/functions/computation-system-v2/scripts/migrate-sectors.js +73 -0
  46. package/functions/computation-system-v2/test/test-dispatcher.js +317 -0
  47. package/functions/computation-system-v2/test/test-framework.js +500 -0
  48. package/functions/computation-system-v2/test/test-real-execution.js +166 -0
  49. package/functions/computation-system-v2/test/test-real-integration.js +194 -0
  50. package/functions/computation-system-v2/test/test-refactor-e2e.js +131 -0
  51. package/functions/computation-system-v2/test/test-results.json +31 -0
  52. package/functions/computation-system-v2/test/test-risk-metrics-computation.js +329 -0
  53. package/functions/computation-system-v2/test/test-scheduler.js +204 -0
  54. package/functions/computation-system-v2/test/test-storage.js +449 -0
  55. package/functions/orchestrator/index.js +24 -30
  56. package/index.js +8 -29
  57. package/package.json +3 -2
  58. package/functions/computation-system/WorkflowOrchestrator.js +0 -213
  59. package/functions/computation-system/config/monitoring_config.js +0 -31
  60. package/functions/computation-system/config/validation_overrides.js +0 -10
  61. package/functions/computation-system/context/ContextFactory.js +0 -143
  62. package/functions/computation-system/context/ManifestBuilder.js +0 -379
  63. package/functions/computation-system/data/AvailabilityChecker.js +0 -236
  64. package/functions/computation-system/data/CachedDataLoader.js +0 -325
  65. package/functions/computation-system/data/DependencyFetcher.js +0 -455
  66. package/functions/computation-system/executors/MetaExecutor.js +0 -279
  67. package/functions/computation-system/executors/PriceBatchExecutor.js +0 -108
  68. package/functions/computation-system/executors/StandardExecutor.js +0 -465
  69. package/functions/computation-system/helpers/computation_dispatcher.js +0 -750
  70. package/functions/computation-system/helpers/computation_worker.js +0 -375
  71. package/functions/computation-system/helpers/monitor.js +0 -64
  72. package/functions/computation-system/helpers/on_demand_helpers.js +0 -154
  73. package/functions/computation-system/layers/extractors.js +0 -1097
  74. package/functions/computation-system/layers/index.js +0 -40
  75. package/functions/computation-system/layers/mathematics.js +0 -522
  76. package/functions/computation-system/layers/profiling.js +0 -537
  77. package/functions/computation-system/layers/validators.js +0 -170
  78. package/functions/computation-system/legacy/AvailabilityCheckerOld.js +0 -388
  79. package/functions/computation-system/legacy/CachedDataLoaderOld.js +0 -357
  80. package/functions/computation-system/legacy/DependencyFetcherOld.js +0 -478
  81. package/functions/computation-system/legacy/MetaExecutorold.js +0 -364
  82. package/functions/computation-system/legacy/StandardExecutorold.js +0 -476
  83. package/functions/computation-system/legacy/computation_dispatcherold.js +0 -944
  84. package/functions/computation-system/logger/logger.js +0 -297
  85. package/functions/computation-system/persistence/ContractValidator.js +0 -81
  86. package/functions/computation-system/persistence/FirestoreUtils.js +0 -56
  87. package/functions/computation-system/persistence/ResultCommitter.js +0 -283
  88. package/functions/computation-system/persistence/ResultsValidator.js +0 -130
  89. package/functions/computation-system/persistence/RunRecorder.js +0 -142
  90. package/functions/computation-system/persistence/StatusRepository.js +0 -52
  91. package/functions/computation-system/reporter_epoch.js +0 -6
  92. package/functions/computation-system/scripts/UpdateContracts.js +0 -128
  93. package/functions/computation-system/services/SnapshotService.js +0 -148
  94. package/functions/computation-system/simulation/Fabricator.js +0 -285
  95. package/functions/computation-system/simulation/SeededRandom.js +0 -41
  96. package/functions/computation-system/simulation/SimRunner.js +0 -51
  97. package/functions/computation-system/system_epoch.js +0 -2
  98. package/functions/computation-system/tools/BuildReporter.js +0 -531
  99. package/functions/computation-system/tools/ContractDiscoverer.js +0 -144
  100. package/functions/computation-system/tools/DeploymentValidator.js +0 -536
  101. package/functions/computation-system/tools/FinalSweepReporter.js +0 -322
  102. package/functions/computation-system/topology/HashManager.js +0 -55
  103. package/functions/computation-system/topology/ManifestLoader.js +0 -47
  104. package/functions/computation-system/utils/data_loader.js +0 -675
  105. package/functions/computation-system/utils/schema_capture.js +0 -121
  106. package/functions/computation-system/utils/utils.js +0 -188
@@ -1,537 +0,0 @@
1
- /**
2
- * @fileoverview Profiling Layer - Intelligence Engine (V6)
3
- * Encapsulates advanced behavioral profiling, psychological scoring, and classification schemas.
4
- * UPDATED: Added AnomalyDetector, SimilarityEngine, and ContentAnalysis.
5
- */
6
-
7
- const SCHEMAS = {
8
- USER_TYPES: { NORMAL: 'normal', SPECULATOR: 'speculator', SIGNED_IN: 'SIGNED_IN_USER', POPULAR: 'POPULAR_INVESTOR' },
9
- STYLES: {
10
- INVESTOR: 'Investor',
11
- SWING_TRADER: 'Swing Trader',
12
- DAY_TRADER: 'Day Trader',
13
- SCALPER: 'Scalper'
14
- },
15
- LABELS: {
16
- ELITE: 'Elite',
17
- SMART: 'Smart Money',
18
- NEUTRAL: 'Neutral',
19
- DUMB: 'Dumb Money',
20
- GAMBLER: 'Gambler'
21
- }
22
- };
23
-
24
- class AnomalyDetector {
25
- /**
26
- * Detects unusual behavior by comparing current portfolio state to historical averages (30D).
27
- * @param {Array} historyTrades - Full trade history
28
- * @param {Array} currentPositions - Current portfolio positions
29
- * @param {Object} mappings - Sector mappings
30
- * @returns {Object} { anomalies: [], warnings: [] }
31
- */
32
- static detect(historyTrades, currentPositions, mappings) {
33
- const anomalies = [];
34
- const warnings = [];
35
-
36
- if (!historyTrades || historyTrades.length < 10) return { anomalies, warnings };
37
-
38
- // 1. Calculate Historical Baselines (last 30 days vs older)
39
- const now = new Date();
40
- const thirtyDaysAgo = new Date(now.getTime() - (30 * 86400000));
41
-
42
- const recentTrades = historyTrades.filter(t => new Date(t.OpenDateTime) > thirtyDaysAgo);
43
- const allLeverage = historyTrades.map(t => t.Leverage || 1);
44
- const avgLeverage = allLeverage.reduce((a,b) => a+b, 0) / allLeverage.length;
45
-
46
- // 2. Check for Leverage Anomalies in Current Positions
47
- for (const pos of currentPositions) {
48
- const currentLev = pos.Leverage || 1;
49
- if (currentLev > avgLeverage * 1.5 && currentLev > 2) {
50
- warnings.push(`High Leverage Alert: Position ${pos.InstrumentID} has ${currentLev}x leverage (Avg: ${avgLeverage.toFixed(1)}x)`);
51
- }
52
- }
53
-
54
- // 3. Check for New Sector Exposure
55
- const historicalSectors = new Set();
56
- historyTrades.forEach(t => {
57
- const sector = mappings.instrumentToSector[t.InstrumentID];
58
- if (sector) historicalSectors.add(sector);
59
- });
60
-
61
- for (const pos of currentPositions) {
62
- const sector = mappings.instrumentToSector[pos.InstrumentID];
63
- if (sector && !historicalSectors.has(sector)) {
64
- anomalies.push(`New Sector Entry: User entered ${sector} sector for the first time.`);
65
- historicalSectors.add(sector); // suppress duplicate alerts
66
- }
67
- }
68
-
69
- return { anomalies, warnings };
70
- }
71
- }
72
-
73
- class SimilarityEngine {
74
- /**
75
- * Finds similar investors based on portfolio composition and risk profile.
76
- * @param {Object} targetUserContext - The user to match
77
- * @param {Array<Object>} candidateContexts - List of other users to compare against
78
- * @param {number} limit - Max results
79
- */
80
- static findPeers(targetUserContext, candidateContexts, limit = 5) {
81
- const targetVector = this._buildFeatureVector(targetUserContext);
82
- const scores = [];
83
-
84
- for (const candidate of candidateContexts) {
85
- // Skip self
86
- if (candidate.user.id === targetUserContext.user.id) continue;
87
-
88
- const candidateVector = this._buildFeatureVector(candidate);
89
- const similarity = this._cosineSimilarity(targetVector, candidateVector);
90
-
91
- scores.push({
92
- userId: candidate.user.id,
93
- score: similarity,
94
- matches: this._explainMatch(targetVector, candidateVector)
95
- });
96
- }
97
-
98
- return scores.sort((a,b) => b.score - a.score).slice(0, limit);
99
- }
100
-
101
- static _buildFeatureVector(ctx) {
102
- // Simplified feature extraction
103
- const portfolio = ctx.user.portfolio.today;
104
- const positions = portfolio.AggregatedPositions || portfolio.PublicPositions || [];
105
-
106
- // 1. Sector Weights
107
- const sectorMap = {};
108
- let totalVal = 0;
109
- positions.forEach(p => {
110
- // Assume mappings are available in ctx or extracted beforehand
111
- // For this snippet, we use InstrumentID as a proxy for asset overlap if sector missing
112
- const val = p.Value || 0;
113
- totalVal += val;
114
- sectorMap[p.InstrumentID] = (sectorMap[p.InstrumentID] || 0) + val;
115
- });
116
-
117
- // Normalize
118
- const vector = {};
119
- if (totalVal > 0) {
120
- Object.keys(sectorMap).forEach(k => vector[k] = sectorMap[k] / totalVal);
121
- }
122
- return vector;
123
- }
124
-
125
- static _cosineSimilarity(vecA, vecB) {
126
- const keys = new Set([...Object.keys(vecA), ...Object.keys(vecB)]);
127
- let dot = 0, magA = 0, magB = 0;
128
- keys.forEach(k => {
129
- const valA = vecA[k] || 0;
130
- const valB = vecB[k] || 0;
131
- dot += valA * valB;
132
- magA += valA * valA;
133
- magB += valB * valB;
134
- });
135
- if (magA === 0 || magB === 0) return 0;
136
- return dot / (Math.sqrt(magA) * Math.sqrt(magB));
137
- }
138
-
139
- static _explainMatch(vecA, vecB) {
140
- // Find top overlapping keys
141
- const overlaps = [];
142
- Object.keys(vecA).forEach(k => {
143
- if (vecB[k]) overlaps.push(k);
144
- });
145
- return overlaps.slice(0, 3); // Top 3 common instruments
146
- }
147
- }
148
-
149
- class SmartMoneyScorer {
150
- static _correlation(x, y) {
151
- if (!x || !y || x.length !== y.length || x.length < 2) return 0;
152
- const n = x.length;
153
- let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0;
154
- for (let i = 0; i < n; i++) {
155
- sumX += x[i]; sumY += y[i];
156
- sumXY += x[i] * y[i];
157
- sumX2 += x[i] * x[i]; sumY2 += y[i] * y[i];
158
- }
159
- const numerator = (n * sumXY) - (sumX * sumY);
160
- const denominator = Math.sqrt(((n * sumX2) - (sumX * sumX)) * ((n * sumY2) - (sumY * sumY)));
161
- return (denominator === 0) ? 0 : numerator / denominator;
162
- }
163
-
164
- static scorePortfolio(portfolio, userType, prices, mappings, math) {
165
- const positions = math.extract.getPositions(portfolio, userType);
166
- if (!positions || positions.length === 0) return { score: 0, label: SCHEMAS.LABELS.NEUTRAL };
167
-
168
- let totalInvested = 0;
169
- let weightedPnL = 0;
170
- let shortInvested = 0;
171
- let shortPnL = 0;
172
-
173
- const weights = [];
174
- const pnls = [];
175
- const sectors = new Set();
176
- const tickers = new Set();
177
-
178
- for (const pos of positions) {
179
- const invested = math.extract.getPositionWeight(pos, userType);
180
- const pnl = math.extract.getNetProfit(pos);
181
- const instId = math.extract.getInstrumentId(pos);
182
- const isShort = math.extract.getDirection(pos) === 'Sell';
183
-
184
- const sector = mappings.instrumentToSector[instId];
185
- const ticker = mappings.instrumentToTicker[instId];
186
-
187
- if (invested > 0) {
188
- totalInvested += invested;
189
- weightedPnL += (pnl * invested);
190
- weights.push(invested);
191
- pnls.push(pnl);
192
-
193
- if (sector) sectors.add(sector);
194
- if (ticker) tickers.add(ticker);
195
-
196
- if (isShort) {
197
- shortInvested += invested;
198
- shortPnL += (pnl * invested);
199
- }
200
- }
201
- }
202
-
203
- if (totalInvested === 0) return { score: 0, label: SCHEMAS.LABELS.NEUTRAL };
204
-
205
- const avgPnL = weightedPnL / totalInvested;
206
- const allocEfficiency = this._correlation(weights, pnls);
207
-
208
- let hhi = 0;
209
- for (const w of weights) {
210
- const share = w / totalInvested;
211
- hhi += (share * share);
212
- }
213
-
214
- const shortRatio = shortInvested / totalInvested;
215
- const avgShortPnL = shortInvested > 0 ? shortPnL / shortInvested : 0;
216
-
217
- let score = 50;
218
-
219
- if (allocEfficiency > 0.5) score += 20;
220
- else if (allocEfficiency < -0.3) score -= 15;
221
-
222
- if (avgPnL > 5) score += 10;
223
- if (avgPnL > 20) score += 10;
224
- if (avgPnL < -10) score -= 10;
225
- if (avgPnL < -25) score -= 15;
226
-
227
- if (hhi > 0.3) {
228
- if (avgPnL > 5) score += 10;
229
- else if (avgPnL < -5) score -= 10;
230
- }
231
-
232
- if (sectors.size >= 4) score += 5;
233
-
234
- if (shortRatio > 0.1) {
235
- if (avgShortPnL > 0) score += 10;
236
- else score -= 10;
237
- }
238
-
239
- return {
240
- score: Math.max(0, Math.min(100, score)),
241
- metrics: { allocEfficiency, hhi, avgPnL, shortRatio, sectorCount: sectors.size }
242
- };
243
- }
244
-
245
- static scoreHistory(historyDoc, prices, mappings, math) {
246
- const trades = historyDoc?.PublicHistoryPositions || [];
247
-
248
- if (trades.length < 5) return { score: 0, label: SCHEMAS.LABELS.NEUTRAL };
249
- const validTrades = trades.filter(t => t.OpenDateTime && t.CloseDateTime && t.InstrumentID);
250
- if (validTrades.length < 5) return { score: 0, label: SCHEMAS.LABELS.NEUTRAL };
251
-
252
- let wins = 0, losses = 0;
253
- let totalWinPct = 0, totalLossPct = 0;
254
- let entryScores = [];
255
- const assetsTraded = new Map();
256
-
257
- validTrades.sort((a, b) => new Date(a.OpenDateTime) - new Date(b.OpenDateTime));
258
- const firstDate = new Date(validTrades[0].OpenDateTime);
259
- const lastDate = new Date(validTrades[validTrades.length-1].OpenDateTime);
260
- const daysActive = Math.max(1, (lastDate - firstDate) / 86400000);
261
-
262
- for (const t of validTrades) {
263
- const ticker = mappings.instrumentToTicker[t.InstrumentID];
264
-
265
- if (!assetsTraded.has(t.InstrumentID)) assetsTraded.set(t.InstrumentID, { count: 0, pnl: 0 });
266
- const assetStat = assetsTraded.get(t.InstrumentID);
267
- assetStat.count++;
268
- assetStat.pnl += t.NetProfit;
269
-
270
- if (t.NetProfit > 0) { wins++; totalWinPct += t.NetProfit; }
271
- else { losses++; totalLossPct += Math.abs(t.NetProfit); }
272
-
273
- if (ticker && prices) {
274
- const priceHist = math.priceExtractor.getHistory(prices, ticker);
275
- if (priceHist && priceHist.length > 0) {
276
- const eff = ExecutionAnalytics.calculateEfficiency(t.OpenRate, priceHist, t.OpenDateTime, t.IsBuy ? 'Buy' : 'Sell');
277
- entryScores.push(eff);
278
- }
279
- }
280
- }
281
-
282
- const avgWin = wins > 0 ? totalWinPct / wins : 0;
283
- const avgLoss = losses > 0 ? totalLossPct / losses : 1;
284
- const profitFactor = (wins * avgWin) / Math.max(1, (losses * avgLoss));
285
-
286
- const avgEntrySkill = entryScores.length > 0 ? math.compute.average(entryScores) : 0.5;
287
-
288
- const totalTrades = validTrades.length;
289
- const uniqueAssets = assetsTraded.size;
290
- const specializationRatio = 1 - (uniqueAssets / totalTrades);
291
-
292
- const tradesPerDay = totalTrades / daysActive;
293
-
294
- let revengeScore = 0;
295
- for (const [id, stat] of assetsTraded.entries()) {
296
- if (stat.pnl < -20 && stat.count > 5) revengeScore += 1;
297
- }
298
-
299
- let score = 50;
300
-
301
- if (profitFactor > 1.2) score += 10;
302
- if (profitFactor > 2.0) score += 15;
303
- if (profitFactor < 0.8) score -= 15;
304
-
305
- if (avgEntrySkill > 0.7) score += 10;
306
- if (avgEntrySkill < 0.3) score -= 10;
307
-
308
- if (specializationRatio > 0.6) score += 5;
309
- if (specializationRatio < 0.1 && totalTrades > 20) score -= 5;
310
-
311
- if (tradesPerDay > 10 && profitFactor < 1.0) score -= 10;
312
-
313
- if (revengeScore > 0) score -= (revengeScore * 5);
314
-
315
- return {
316
- score: Math.max(0, Math.min(100, score)),
317
- metrics: { profitFactor, avgEntrySkill, specializationRatio, tradesPerDay, revengeScore }
318
- };
319
- }
320
-
321
- static scoreHybrid(context) {
322
- const { user, prices, mappings, math } = context;
323
-
324
- const pScore = this.scorePortfolio(user.portfolio.today, user.type, prices, mappings, math);
325
- const hScore = this.scoreHistory(user.history.today, prices, mappings, math);
326
-
327
- let finalScore = 50;
328
- let method = 'Neutral';
329
-
330
- const hasHistory = hScore && hScore.score > 0;
331
- const hasPortfolio = pScore && pScore.score > 0;
332
-
333
- if (hasHistory && hasPortfolio) {
334
- finalScore = (hScore.score * 0.6) + (pScore.score * 0.4);
335
- method = 'Hybrid';
336
- } else if (hasHistory) {
337
- finalScore = hScore.score;
338
- method = 'HistoryOnly';
339
- } else if (hasPortfolio) {
340
- finalScore = pScore.score;
341
- method = 'PortfolioOnly';
342
- }
343
-
344
- let label = SCHEMAS.LABELS.NEUTRAL;
345
- if (finalScore >= 80) label = SCHEMAS.LABELS.ELITE;
346
- else if (finalScore >= 65) label = SCHEMAS.LABELS.SMART;
347
- else if (finalScore <= 35) label = SCHEMAS.LABELS.GAMBLER;
348
- else if (finalScore <= 50) label = SCHEMAS.LABELS.DUMB;
349
-
350
- return {
351
- totalScore: Math.round(finalScore),
352
- label: label,
353
- method: method,
354
- components: {
355
- portfolio: pScore,
356
- history: hScore
357
- }
358
- };
359
- }
360
- }
361
-
362
- class CognitiveBiases {
363
- static calculateAnchoringScore(openPositions, thresholdPct = 2.0, minDaysHeld = 14) {
364
- if (!openPositions || openPositions.length === 0) return 0;
365
- let anchoredCount = 0, validPositions = 0;
366
- const now = Date.now(), msPerDay = 86400000;
367
- for (const pos of openPositions) {
368
- if (pos.OpenDateTime) {
369
- validPositions++;
370
- const ageDays = (now - new Date(pos.OpenDateTime).getTime()) / msPerDay;
371
- if (ageDays > minDaysHeld && Math.abs(pos.NetProfit) < thresholdPct) { anchoredCount++; }
372
- }
373
- }
374
- return validPositions > 0 ? (anchoredCount / validPositions) : 0;
375
- }
376
-
377
- static calculateDispositionEffect(historyTrades) {
378
- let winDur = 0, winCount = 0, lossDur = 0, lossCount = 0;
379
- for (const t of historyTrades) {
380
- if (!t.OpenDateTime || !t.CloseDateTime) continue;
381
- const dur = (new Date(t.CloseDateTime) - new Date(t.OpenDateTime)) / 3600000;
382
- if (t.NetProfit > 0) { winDur += dur; winCount++; } else if (t.NetProfit < 0) { lossDur += dur; lossCount++; }
383
- }
384
- const avgWinHold = winCount > 0 ? winDur / winCount : 0;
385
- const avgLossHold = lossCount > 0 ? lossDur / lossCount : 0;
386
- if (avgWinHold === 0) return 2.0;
387
- return avgLossHold / avgWinHold;
388
- }
389
- }
390
-
391
- class SkillAttribution {
392
- static calculateSelectionAlpha(userPositions, dailyInsights) {
393
- let totalAlpha = 0, count = 0;
394
- for (const pos of userPositions) {
395
- const instrumentId = pos.InstrumentID;
396
- let insight = Array.isArray(dailyInsights) ? dailyInsights.find(i => i.instrumentId === instrumentId) : null;
397
- if (insight && typeof insight.growth === 'number') {
398
- totalAlpha += (pos.NetProfit - insight.growth);
399
- count++;
400
- }
401
- }
402
- return count > 0 ? totalAlpha / count : 0;
403
- }
404
- }
405
-
406
- class ExecutionAnalytics {
407
- static calculateEfficiency(price, priceHistory, date, direction, windowDays = 7) {
408
- if (!priceHistory || priceHistory.length === 0) return 0.5;
409
- const targetDate = new Date(date);
410
- const start = new Date(targetDate); start.setDate(start.getDate() - windowDays);
411
- const end = new Date(targetDate); end.setDate(end.getDate() + windowDays);
412
- const pricesInWindow = priceHistory.filter(p => { const d = new Date(p.date); return d >= start && d <= end; }).map(p => p.price);
413
- if (pricesInWindow.length === 0) return 0.5;
414
- const minP = Math.min(...pricesInWindow);
415
- const maxP = Math.max(...pricesInWindow);
416
- const range = maxP - minP;
417
- if (range === 0) return 0.5;
418
- const location = (price - minP) / range;
419
- if (direction === 'Buy') { return 1 - location; } else { return location; }
420
- }
421
- static calculateLossTolerance(realizedPnL, maxDrawdown) {
422
- if (maxDrawdown === 0) return 1;
423
- return (realizedPnL - maxDrawdown) / Math.abs(maxDrawdown);
424
- }
425
- }
426
-
427
- /**
428
- * UPGRADE: Risk Geometry Class
429
- * Contains Convex Hull (Monotone Chain) for Efficient Frontier analysis.
430
- */
431
- class RiskGeometry {
432
- static crossProduct(o, a, b) {
433
- return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
434
- }
435
-
436
- /**
437
- * Computes the Convex Hull of a set of 2D points using Monotone Chain algorithm.
438
- * @param {Array<{x: number, y: number}>} points
439
- * @returns {Array<{x: number, y: number}>} Points on the hull
440
- */
441
- static computeConvexHull(points) {
442
- if (points.length <= 1) return points;
443
-
444
- // Sort by x coordinate (and y if x is same)
445
- points.sort((a, b) => a.x === b.x ? a.y - b.y : a.x - b.x);
446
-
447
- const lower = [];
448
- for (const p of points) {
449
- while (lower.length >= 2 && this.crossProduct(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
450
- lower.pop();
451
- }
452
- lower.push(p);
453
- }
454
-
455
- const upper = [];
456
- for (let i = points.length - 1; i >= 0; i--) {
457
- const p = points[i];
458
- while (upper.length >= 2 && this.crossProduct(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
459
- upper.pop();
460
- }
461
- upper.push(p);
462
- }
463
-
464
- upper.pop();
465
- lower.pop();
466
- return lower.concat(upper);
467
- }
468
- }
469
-
470
- class Psychometrics {
471
- static computeDispositionSkew(historyTrades, currentPositions) {
472
- const getMedian = (arr) => { if (!arr.length) return 0; const sorted = [...arr].sort((a, b) => a - b); const mid = Math.floor(sorted.length / 2); return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; };
473
- const realized = historyTrades.map(t => t.NetProfit);
474
- const unrealized = currentPositions.map(p => p.NetProfit);
475
- if (realized.length < 5 || unrealized.length < 5) return 0;
476
- return getMedian(realized) - getMedian(unrealized);
477
- }
478
- static detectRevengeTrading(historyTrades) {
479
- let riskSpikes = 0; let losses = 0; if (historyTrades.length === 0) return 0;
480
- const avgLev = historyTrades.reduce((sum, t) => sum + (t.Leverage || 1), 0) / historyTrades.length;
481
- for (let i = 1; i < historyTrades.length; i++) {
482
- const prev = historyTrades[i-1]; const curr = historyTrades[i];
483
- if (prev.NetProfit < 0) { losses++; if ((curr.Leverage || 1) > (avgLev * 1.5)) { riskSpikes++; } }
484
- }
485
- return losses > 0 ? (riskSpikes / losses) : 0;
486
- }
487
- }
488
-
489
- class AdaptiveAnalytics {
490
- static analyzeDrawdownAdaptation(tradeHistory, drawdownThreshold = -15) {
491
- let adaptationScore = 0; let eventCount = 0;
492
- for (let i = 0; i < tradeHistory.length - 3; i++) {
493
- if (tradeHistory[i].NetProfit < drawdownThreshold) {
494
- eventCount++; const nextTrades = tradeHistory.slice(i+1, i+4);
495
- const prevLev = tradeHistory[i].Leverage || 1;
496
- const nextLevAvg = nextTrades.reduce((s, t) => s + (t.Leverage||1), 0) / 3;
497
- if (nextLevAvg < prevLev) adaptationScore += 1; else if (nextLevAvg > prevLev * 1.5) adaptationScore -= 2;
498
- const lostInstrument = tradeHistory[i].InstrumentID;
499
- const stuckToSame = nextTrades.every(t => t.InstrumentID === lostInstrument);
500
- if (!stuckToSame) adaptationScore += 0.5;
501
- }
502
- }
503
- return eventCount > 0 ? (adaptationScore / eventCount) : 0;
504
- }
505
- }
506
-
507
- class UserClassifier {
508
- static classify(context) {
509
- const result = SmartMoneyScorer.scoreHybrid(context);
510
- return {
511
- intelligence: {
512
- label: result.label,
513
- score: result.totalScore,
514
- isSmart: result.totalScore >= 65
515
- },
516
- style: { primary: SCHEMAS.STYLES.INVESTOR },
517
- metrics: {
518
- profitFactor: result.components.history?.metrics?.profitFactor || 0,
519
- allocEfficiency: result.components.portfolio?.metrics?.allocEfficiency || 0
520
- }
521
- };
522
- }
523
- }
524
-
525
- module.exports = {
526
- SCHEMAS,
527
- UserClassifier,
528
- SmartMoneyScorer,
529
- ExecutionAnalytics,
530
- Psychometrics,
531
- AdaptiveAnalytics,
532
- CognitiveBiases,
533
- SkillAttribution,
534
- RiskGeometry,
535
- AnomalyDetector,
536
- SimilarityEngine
537
- };