aiden-shared-calculations-unified 1.0.35 → 1.0.37

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 (58) hide show
  1. package/README.MD +77 -77
  2. package/calculations/activity/historical/activity_by_pnl_status.js +85 -85
  3. package/calculations/activity/historical/daily_asset_activity.js +85 -85
  4. package/calculations/activity/historical/daily_user_activity_tracker.js +144 -144
  5. package/calculations/activity/historical/speculator_adjustment_activity.js +76 -76
  6. package/calculations/asset_metrics/asset_position_size.js +57 -57
  7. package/calculations/backtests/strategy-performance.js +229 -245
  8. package/calculations/behavioural/historical/asset_crowd_flow.js +165 -165
  9. package/calculations/behavioural/historical/drawdown_response.js +58 -58
  10. package/calculations/behavioural/historical/dumb-cohort-flow.js +217 -249
  11. package/calculations/behavioural/historical/gain_response.js +57 -57
  12. package/calculations/behavioural/historical/in_loss_asset_crowd_flow.js +98 -98
  13. package/calculations/behavioural/historical/in_profit_asset_crowd_flow.js +99 -99
  14. package/calculations/behavioural/historical/paper_vs_diamond_hands.js +39 -39
  15. package/calculations/behavioural/historical/position_count_pnl.js +67 -67
  16. package/calculations/behavioural/historical/smart-cohort-flow.js +217 -250
  17. package/calculations/behavioural/historical/smart_money_flow.js +165 -165
  18. package/calculations/behavioural/historical/user-investment-profile.js +358 -412
  19. package/calculations/capital_flow/historical/crowd-cash-flow-proxy.js +121 -121
  20. package/calculations/capital_flow/historical/deposit_withdrawal_percentage.js +117 -117
  21. package/calculations/capital_flow/historical/new_allocation_percentage.js +49 -49
  22. package/calculations/insights/daily_bought_vs_sold_count.js +55 -55
  23. package/calculations/insights/daily_buy_sell_sentiment_count.js +49 -49
  24. package/calculations/insights/daily_ownership_delta.js +55 -55
  25. package/calculations/insights/daily_total_positions_held.js +39 -39
  26. package/calculations/meta/capital_deployment_strategy.js +129 -137
  27. package/calculations/meta/capital_liquidation_performance.js +121 -163
  28. package/calculations/meta/capital_vintage_performance.js +121 -158
  29. package/calculations/meta/cash-flow-deployment.js +110 -124
  30. package/calculations/meta/cash-flow-liquidation.js +126 -142
  31. package/calculations/meta/crowd_sharpe_ratio_proxy.js +83 -91
  32. package/calculations/meta/profit_cohort_divergence.js +77 -91
  33. package/calculations/meta/smart-dumb-divergence-index.js +116 -138
  34. package/calculations/meta/social_flow_correlation.js +99 -125
  35. package/calculations/pnl/asset_pnl_status.js +46 -46
  36. package/calculations/pnl/historical/profitability_migration.js +57 -57
  37. package/calculations/pnl/historical/user_profitability_tracker.js +117 -117
  38. package/calculations/pnl/profitable_and_unprofitable_status.js +64 -64
  39. package/calculations/sectors/historical/diversification_pnl.js +76 -76
  40. package/calculations/sectors/historical/sector_rotation.js +67 -67
  41. package/calculations/sentiment/historical/crowd_conviction_score.js +80 -80
  42. package/calculations/socialPosts/social-asset-posts-trend.js +52 -52
  43. package/calculations/socialPosts/social-top-mentioned-words.js +102 -102
  44. package/calculations/socialPosts/social-topic-interest-evolution.js +53 -53
  45. package/calculations/socialPosts/social-word-mentions-trend.js +62 -62
  46. package/calculations/socialPosts/social_activity_aggregation.js +103 -103
  47. package/calculations/socialPosts/social_event_correlation.js +121 -121
  48. package/calculations/socialPosts/social_sentiment_aggregation.js +114 -114
  49. package/calculations/speculators/historical/risk_appetite_change.js +54 -54
  50. package/calculations/speculators/historical/tsl_effectiveness.js +74 -74
  51. package/index.js +33 -33
  52. package/package.json +32 -32
  53. package/utils/firestore_utils.js +76 -76
  54. package/utils/price_data_provider.js +142 -142
  55. package/utils/sector_mapping_provider.js +74 -74
  56. package/calculations/capital_flow/historical/reallocation_increase_percentage.js +0 -63
  57. package/calculations/speculators/stop_loss_distance_by_sector_short_long_breakdown.js +0 -91
  58. package/calculations/speculators/stop_loss_distance_by_ticker_short_long_breakdown.js +0 -73
@@ -1,159 +1,122 @@
1
- /**
2
- * @fileoverview Meta-calculation (Pass 3) that tracks the performance
3
- * of capital "vintages" by analyzing the market returns of assets
4
- * that were bought following a crowd-wide deposit signal.
5
- *
6
- * This answers: "Does capital deployed from a fresh deposit event
7
- * outperform capital deployed earlier?"
8
- */
9
-
10
- class CapitalVintagePerformance {
11
- constructor() {
12
- // How many days to look back/forward to measure performance
13
- this.PERFORMANCE_WINDOW_DAYS = 7;
14
- this.dependenciesLoaded = false;
15
- this.priceMap = null;
16
- this.tickerToIdMap = null;
17
- }
18
-
19
- /**
20
- * Helper to load all dependencies in parallel
21
- */
22
- async _loadDependencies(calculationUtils) {
23
- if (this.dependenciesLoaded) return;
24
-
25
- const { loadAllPriceData, loadInstrumentMappings } = calculationUtils;
26
-
27
- const [priceData, mappings] = await Promise.all([
28
- loadAllPriceData(),
29
- loadInstrumentMappings()
30
- ]);
31
-
32
- this.priceMap = priceData;
33
-
34
- // Create a reverse map for easy lookup
35
- this.tickerToIdMap = {};
36
- if (mappings && mappings.instrumentToTicker) {
37
- for (const [id, ticker] of Object.entries(mappings.instrumentToTicker)) {
38
- this.tickerToIdMap[ticker] = id;
39
- }
40
- }
41
-
42
- this.dependenciesLoaded = true;
43
- }
44
-
45
- /**
46
- * Helper to get a date string X days from a base date
47
- */
48
- _getDateStr(baseDateStr, daysOffset) {
49
- const date = new Date(baseDateStr + 'T00:00:00Z');
50
- date.setUTCDate(date.getUTCDate() + daysOffset);
51
- return date.toISOString().slice(0, 10);
52
- }
53
-
54
- /**
55
- * Helper to calculate the average return of a basket of assets
56
- * over a specified period.
57
- */
58
- _calculateBasketPerformance(assets, startDateStr, endDateStr) {
59
- if (!assets || assets.length === 0) {
60
- return 0;
61
- }
62
-
63
- let totalReturn = 0;
64
- let validAssets = 0;
65
-
66
- for (const asset of assets) {
67
- const ticker = asset.ticker;
68
- const instrumentId = this.tickerToIdMap[ticker];
69
-
70
- if (!instrumentId || !this.priceMap[instrumentId]) {
71
- continue; // No price data for this ticker
72
- }
73
-
74
- const startPrice = this.priceMap[instrumentId][startDateStr];
75
- const endPrice = this.priceMap[instrumentId][endDateStr];
76
-
77
- if (startPrice && endPrice && startPrice > 0) {
78
- const assetReturn = (endPrice - startPrice) / startPrice;
79
- totalReturn += assetReturn;
80
- validAssets++;
81
- }
82
- }
83
-
84
- if (validAssets === 0) return 0;
85
- return (totalReturn / validAssets) * 100; // Return as percentage
86
- }
87
-
88
- /**
89
- * @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
90
- * @param {object} dependencies The shared dependencies (db, logger, calculationUtils).
91
- * @param {object} config The computation system configuration.
92
- * @returns {Promise<object|null>} The analysis result or null.
93
- */
94
- async process(dateStr, dependencies, config) {
95
- const { db, logger, calculationUtils } = dependencies;
96
-
97
- // 1. Load all price/mapping data
98
- await this._loadDependencies(calculationUtils);
99
-
100
- // 2. Define and fetch dependency: cash-flow-deployment
101
- const depRef = db.collection(config.resultsCollection).doc(dateStr)
102
- .collection('results').doc('meta')
103
- .collection('computations').doc('cash-flow-deployment');
104
-
105
- const snapshot = await depRef.get();
106
-
107
- if (!snapshot.exists || snapshot.data().status !== 'analysis_complete') {
108
- logger.log('WARN', `[CapitalVintage] Skipping ${dateStr}, no valid 'cash-flow-deployment' data found.`);
109
- return null;
110
- }
111
-
112
- const data = snapshot.data();
113
- const topAssets = data.top_deployment_assets; // [{ ticker: 'AAPL', ... }]
114
- const signalDate = data.signal_date; // The day the deposit signal occurred
115
- const deploymentDate = data.analysis_date; // The day the capital was spent (dateStr)
116
-
117
- if (!topAssets || topAssets.length === 0) {
118
- logger.log('INFO', `[CapitalVintage] No top assets deployed on ${dateStr}.`);
119
- return { status: 'no_deployment' };
120
- }
121
-
122
- // 3. Define performance windows
123
- // "Before" window: 7 days leading up to the signal
124
- const preSignalStart = this._getDateStr(signalDate, -this.PERFORMANCE_WINDOW_DAYS);
125
- const preSignalEnd = signalDate;
126
-
127
- // "After" window: 7 days starting from the deployment
128
- const postDeployStart = deploymentDate;
129
- const postDeployEnd = this._getDateStr(deploymentDate, this.PERFORMANCE_WINDOW_DAYS);
130
-
131
- // 4. Calculate performance
132
- const preSignalReturnPct = this._calculateBasketPerformance(
133
- topAssets, preSignalStart, preSignalEnd
134
- );
135
-
136
- const postDeploymentReturnPct = this._calculateBasketPerformance(
137
- topAssets, postDeployStart, postDeployEnd
138
- );
139
-
140
- const momentum = postDeploymentReturnPct - preSignalReturnPct;
141
-
142
- return {
143
- status: 'analysis_complete',
144
- signal_date: signalDate,
145
- deployment_date: deploymentDate,
146
- performance_window_days: this.PERFORMANCE_WINDOW_DAYS,
147
- deployed_assets: topAssets.map(a => a.ticker),
148
- pre_signal_return_pct: preSignalReturnPct,
149
- post_deployment_return_pct: postDeploymentReturnPct,
150
- return_momentum: momentum,
151
- interpretation: "Measures the 7-day return of deployed assets *after* deployment vs. 7-days *before* the signal."
152
- };
153
- }
154
-
155
- async getResult() { return null; }
156
- reset() {}
157
- }
158
-
1
+ /**
2
+ * @fileoverview Meta-calculation (Pass 3) that tracks the performance
3
+ * of capital "vintages" by analyzing the market returns of assets
4
+ * that were bought following a crowd-wide deposit signal.
5
+ */
6
+
7
+ class CapitalVintagePerformance {
8
+ constructor() {
9
+ this.PERFORMANCE_WINDOW_DAYS = 7;
10
+ this.dependenciesLoaded = false;
11
+ this.priceMap = null;
12
+ this.tickerToIdMap = null;
13
+ }
14
+
15
+ async _loadDependencies(calculationUtils) {
16
+ if (this.dependenciesLoaded) return;
17
+ const { loadAllPriceData, loadInstrumentMappings } = calculationUtils;
18
+ const [priceData, mappings] = await Promise.all([
19
+ loadAllPriceData(),
20
+ loadInstrumentMappings()
21
+ ]);
22
+ this.priceMap = priceData;
23
+ this.tickerToIdMap = {};
24
+ if (mappings && mappings.instrumentToTicker) {
25
+ for (const [id, ticker] of Object.entries(mappings.instrumentToTicker)) {
26
+ this.tickerToIdMap[ticker] = id;
27
+ }
28
+ }
29
+ this.dependenciesLoaded = true;
30
+ }
31
+
32
+ _getDateStr(baseDateStr, daysOffset) {
33
+ const date = new Date(baseDateStr + 'T00:00:00Z');
34
+ date.setUTCDate(date.getUTCDate() + daysOffset);
35
+ return date.toISOString().slice(0, 10);
36
+ }
37
+
38
+ _calculateBasketPerformance(assets, startDateStr, endDateStr) {
39
+ if (!assets || assets.length === 0) return 0;
40
+ let totalReturn = 0;
41
+ let validAssets = 0;
42
+ for (const asset of assets) {
43
+ const ticker = asset.ticker;
44
+ const instrumentId = this.tickerToIdMap[ticker];
45
+ if (!instrumentId || !this.priceMap[instrumentId]) continue;
46
+ const startPrice = this.priceMap[instrumentId][startDateStr];
47
+ const endPrice = this.priceMap[instrumentId][endDateStr];
48
+ if (startPrice && endPrice && startPrice > 0) {
49
+ const assetReturn = (endPrice - startPrice) / startPrice;
50
+ totalReturn += assetReturn;
51
+ validAssets++;
52
+ }
53
+ }
54
+ if (validAssets === 0) return 0;
55
+ return (totalReturn / validAssets) * 100;
56
+ }
57
+
58
+ /**
59
+ * @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
60
+ * @param {object} dependencies The shared dependencies (db, logger, calculationUtils).
61
+ * @param {object} config The computation system configuration.
62
+ * @param {object} computedDependencies In-memory results from previous passes.
63
+ * @returns {Promise<object|null>} The analysis result or null.
64
+ */
65
+ async process(dateStr, dependencies, config, computedDependencies) {
66
+ const { db, logger, calculationUtils } = dependencies;
67
+
68
+ // 1. Load all price/mapping data
69
+ await this._loadDependencies(calculationUtils);
70
+
71
+ // 2. Get dependency from in-memory cache
72
+ const data = computedDependencies['cash-flow-deployment'];
73
+
74
+ if (!data || data.status !== 'analysis_complete') {
75
+ logger.log('WARN', `[CapitalVintage] Skipping ${dateStr}, no valid 'cash-flow-deployment' data found.`);
76
+ return null;
77
+ }
78
+
79
+ const topAssets = data.top_deployment_assets; // [{ ticker: 'AAPL', ... }]
80
+ const signalDate = data.signal_date; // The day the deposit signal occurred
81
+ const deploymentDate = data.analysis_date; // The day the capital was spent (dateStr)
82
+
83
+ if (!topAssets || topAssets.length === 0) {
84
+ logger.log('INFO', `[CapitalVintage] No top assets deployed on ${dateStr}.`);
85
+ return { status: 'no_deployment' };
86
+ }
87
+
88
+ // 3. Define performance windows
89
+ const preSignalStart = this._getDateStr(signalDate, -this.PERFORMANCE_WINDOW_DAYS);
90
+ const preSignalEnd = signalDate;
91
+ const postDeployStart = deploymentDate;
92
+ const postDeployEnd = this._getDateStr(deploymentDate, this.PERFORMANCE_WINDOW_DAYS);
93
+
94
+ // 4. Calculate performance
95
+ const preSignalReturnPct = this._calculateBasketPerformance(
96
+ topAssets, preSignalStart, preSignalEnd
97
+ );
98
+
99
+ const postDeploymentReturnPct = this._calculateBasketPerformance(
100
+ topAssets, postDeployStart, postDeployEnd
101
+ );
102
+
103
+ const momentum = postDeploymentReturnPct - preSignalReturnPct;
104
+
105
+ return {
106
+ status: 'analysis_complete',
107
+ signal_date: signalDate,
108
+ deployment_date: deploymentDate,
109
+ performance_window_days: this.PERFORMANCE_WINDOW_DAYS,
110
+ deployed_assets: topAssets.map(a => a.ticker),
111
+ pre_signal_return_pct: preSignalReturnPct,
112
+ post_deployment_return_pct: postDeploymentReturnPct,
113
+ return_momentum: momentum,
114
+ interpretation: "Measures the 7-day return of deployed assets *after* deployment vs. 7-days *before* the signal."
115
+ };
116
+ }
117
+
118
+ async getResult() { return null; }
119
+ reset() {}
120
+ }
121
+
159
122
  module.exports = CapitalVintagePerformance;
@@ -1,125 +1,111 @@
1
- const { FieldValue } = require('@google-cloud/firestore');
2
-
3
- class CashFlowDeployment {
4
- constructor() {
5
- this.lookbackDays = 7;
6
- this.correlationWindow = 3;
7
- this.depositSignalThreshold = -0.005; // Formerly -1.0
8
- }
9
-
10
- _getDateStr(baseDate, daysAgo) {
11
- const date = new Date(baseDate + 'T00:00:00Z');
12
- date.setUTCDate(date.getUTCDate() - daysAgo);
13
- return date.toISOString().slice(0, 10);
14
- }
15
-
16
- async process(dateStr, dependencies, config) {
17
- const { db, logger } = dependencies;
18
- const collection = config.resultsCollection;
19
-
20
- // build all needed refs in advance for this day
21
- const dateRefs = [];
22
- const dates = [];
23
-
24
- for (let i = 1; i <= this.lookbackDays; i++) {
25
- const checkDate = this._getDateStr(dateStr, i);
26
- dates.push({ date: checkDate, category: 'capital_flow', computation: 'crowd-cash-flow-proxy' });
27
- }
28
-
29
- // add refs for today's 2 dependencies
30
- dates.push({ date: dateStr, category: 'capital_flow', computation: 'crowd-cash-flow-proxy' });
31
- dates.push({ date: dateStr, category: 'behavioural', computation: 'asset-crowd-flow' });
32
-
33
- // build refs array
34
- const refs = dates.map(d =>
35
- db.collection(collection).doc(d.date)
36
- .collection('results').doc(d.category)
37
- .collection('computations').doc(d.computation)
38
- );
39
-
40
- const snapshots = await db.getAll(...refs);
41
-
42
- // build map(path -> data)
43
- const dataMap = new Map();
44
- snapshots.forEach((snap, idx) => {
45
- if (snap.exists) dataMap.set(idx, snap.data());
46
- });
47
-
48
- // --- START MODIFICATION ---
49
- // Check for THIS DATE's dependencies (cash-flow and asset-flow) FIRST.
50
- // These are at the end of the 'dates' array.
51
- const cashFlowData = dataMap.get(this.lookbackDays);
52
- const assetFlowData = dataMap.get(this.lookbackDays + 1);
53
-
54
- if (!cashFlowData || !assetFlowData) {
55
- logger.log('WARN', `[CashFlowDeployment] Missing critical dependency data (cash-flow or asset-flow) for ${dateStr}. Skipping to allow backfill.`);
56
- return null; // This allows backfill
57
- }
58
- // --- END MODIFICATION ---
59
-
60
- // find deposit signal
61
- let depositSignal = null;
62
- let depositSignalDay = null;
63
-
64
- for (let i = 0; i < this.lookbackDays; i++) {
65
- const flowData = dataMap.get(i);
66
- const dateUsed = dates[i].date;
67
- if (flowData && flowData.cash_flow_effect_proxy < this.depositSignalThreshold) {
68
- depositSignal = flowData;
69
- depositSignalDay = dateUsed;
70
- break;
71
- }
72
- }
73
-
74
- if (!depositSignal) {
75
- return {
76
- status: 'no_signal_found',
77
- lookback_days: this.lookbackDays,
78
- signal_threshold: this.depositSignalThreshold
79
- };
80
- }
81
-
82
- const daysSinceSignal = (new Date(dateStr) - new Date(depositSignalDay)) / (1000 * 60 * 60 * 24);
83
-
84
- if (daysSinceSignal <= 0 || daysSinceSignal > this.correlationWindow) {
85
- return {
86
- status: 'outside_correlation_window',
87
- signal_day: depositSignalDay,
88
- days_since_signal: daysSinceSignal
89
- };
90
- }
91
-
92
- // --- REMOVED ---
93
- // The check for cashFlowData and assetFlowData was here
94
- // but has been moved up.
95
- // --- END REMOVED ---
96
-
97
- const netSpendPct = cashFlowData.components?.trading_effect || 0;
98
- const netDepositPct = Math.abs(depositSignal.cash_flow_effect_proxy);
99
-
100
- const topBuys = Object.entries(assetFlowData)
101
- .filter(([ticker, data]) => data.net_crowd_flow_pct > 0)
102
- .sort(([, a], [, b]) => b.net_crowd_flow_pct - a.net_crowd_flow_pct)
103
- .slice(0, 10)
104
- .map(([ticker, data]) => ({
105
- ticker,
106
- net_flow_pct: data.net_crowd_flow_pct
107
- }));
108
-
109
- return {
110
- status: 'analysis_complete',
111
- analysis_date: dateStr,
112
- signal_date: depositSignalDay,
113
- days_since_signal: daysSinceSignal,
114
- signal_deposit_proxy_pct: netDepositPct,
115
- day_net_spend_pct: netSpendPct,
116
- pct_of_deposit_deployed_today: (netSpendPct / netDepositPct) * 100,
117
- top_deployment_assets: topBuys
118
- };
119
- }
120
-
121
- async getResult() { return null; }
122
- reset() {}
123
- }
124
-
1
+ const { FieldValue } = require('@google-cloud/firestore');
2
+
3
+ class CashFlowDeployment {
4
+ constructor() {
5
+ this.lookbackDays = 7;
6
+ this.correlationWindow = 3;
7
+ this.depositSignalThreshold = -0.005; // Formerly -1.0
8
+ }
9
+
10
+ _getDateStr(baseDate, daysAgo) {
11
+ const date = new Date(baseDate + 'T00:00:00Z');
12
+ date.setUTCDate(date.getUTCDate() - daysAgo);
13
+ return date.toISOString().slice(0, 10);
14
+ }
15
+
16
+ async process(dateStr, dependencies, config, computedDependencies) {
17
+ const { db, logger } = dependencies;
18
+ const collection = config.resultsCollection;
19
+
20
+ // --- MODIFICATION: Get same-day dependencies from cache ---
21
+ const cashFlowData = computedDependencies['crowd-cash-flow-proxy'];
22
+ const assetFlowData = computedDependencies['asset-crowd-flow'];
23
+
24
+ if (!cashFlowData || !assetFlowData) {
25
+ logger.log('WARN', `[CashFlowDeployment] Missing critical in-memory dependency data for ${dateStr}. Skipping.`);
26
+ return null;
27
+ }
28
+ // --- END MODIFICATION ---
29
+
30
+ // --- Historical lookback for signal still uses Firestore ---
31
+ const refs = [];
32
+ const dates = [];
33
+ for (let i = 1; i <= this.lookbackDays; i++) {
34
+ const checkDate = this._getDateStr(dateStr, i);
35
+ dates.push({ date: checkDate, category: 'capital_flow', computation: 'crowd-cash-flow-proxy' });
36
+ }
37
+ const histRefs = dates.map(d =>
38
+ db.collection(collection).doc(d.date)
39
+ .collection('results').doc(d.category)
40
+ .collection('computations').doc(d.computation)
41
+ );
42
+ const snapshots = await db.getAll(...histRefs);
43
+ const dataMap = new Map();
44
+ snapshots.forEach((snap, idx) => {
45
+ if (snap.exists) dataMap.set(idx, snap.data());
46
+ });
47
+ // --- End historical lookback ---
48
+
49
+
50
+ // find deposit signal
51
+ let depositSignal = null;
52
+ let depositSignalDay = null;
53
+
54
+ for (let i = 0; i < this.lookbackDays; i++) {
55
+ const flowData = dataMap.get(i);
56
+ const dateUsed = dates[i].date;
57
+ if (flowData && flowData.cash_flow_effect_proxy < this.depositSignalThreshold) {
58
+ depositSignal = flowData;
59
+ depositSignalDay = dateUsed;
60
+ break;
61
+ }
62
+ }
63
+
64
+ if (!depositSignal) {
65
+ return {
66
+ status: 'no_signal_found',
67
+ lookback_days: this.lookbackDays,
68
+ signal_threshold: this.depositSignalThreshold
69
+ };
70
+ }
71
+
72
+ const daysSinceSignal = (new Date(dateStr) - new Date(depositSignalDay)) / (1000 * 60 * 60 * 24);
73
+
74
+ if (daysSinceSignal <= 0 || daysSinceSignal > this.correlationWindow) {
75
+ return {
76
+ status: 'outside_correlation_window',
77
+ signal_day: depositSignalDay,
78
+ days_since_signal: daysSinceSignal
79
+ };
80
+ }
81
+
82
+ // Use the in-memory data for today
83
+ const netSpendPct = cashFlowData.components?.trading_effect || 0;
84
+ const netDepositPct = Math.abs(depositSignal.cash_flow_effect_proxy);
85
+
86
+ const topBuys = Object.entries(assetFlowData)
87
+ .filter(([ticker, data]) => data.net_crowd_flow_pct > 0)
88
+ .sort(([, a], [, b]) => b.net_crowd_flow_pct - a.net_crowd_flow_pct)
89
+ .slice(0, 10)
90
+ .map(([ticker, data]) => ({
91
+ ticker,
92
+ net_flow_pct: data.net_crowd_flow_pct
93
+ }));
94
+
95
+ return {
96
+ status: 'analysis_complete',
97
+ analysis_date: dateStr,
98
+ signal_date: depositSignalDay,
99
+ days_since_signal: daysSinceSignal,
100
+ signal_deposit_proxy_pct: netDepositPct,
101
+ day_net_spend_pct: netSpendPct,
102
+ pct_of_deposit_deployed_today: (netSpendPct / netDepositPct) * 100,
103
+ top_deployment_assets: topBuys
104
+ };
105
+ }
106
+
107
+ async getResult() { return null; }
108
+ reset() {}
109
+ }
110
+
125
111
  module.exports = CashFlowDeployment;