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,122 +1,122 @@
1
- /**
2
- * @fileoverview Estimates a proxy for net crowd cash flow (Deposits vs. Withdrawals)
3
- * by aggregating portfolio percentage changes across all users.
4
- *
5
- * This calculation is based on the formula:
6
- * Total_Change = P/L_Effect + Trading_Effect + Cash_Flow_Effect
7
- *
8
- * Where:
9
- * - Total_Change = avg_value_day2 - avg_value_day1
10
- * - P/L_Effect = avg_value_day1 - avg_invested_day1
11
- * - Trading_Effect = avg_invested_day2 - avg_invested_day1
12
- *
13
- * We solve for Cash_Flow_Effect, which serves as our proxy.
14
- * A negative value indicates a net DEPOSIT (denominator grew, shrinking all %s).
15
- * A positive value indicates a net WITHDRAWAL (denominator shrank, inflating all %s).
16
- *
17
- * NOTE: This file is a logical duplicate of deposit_withdrawal_percentage.js
18
- * to satisfy the hardcoded 'crowd-cash-flow-proxy' dependency in several
19
- * meta-calculations.
20
- */
21
-
22
- class CrowdCashFlowProxy {
23
- constructor() {
24
- this.total_invested_day1 = 0;
25
- this.total_value_day1 = 0;
26
- this.total_invested_day2 = 0;
27
- this.total_value_day2 = 0;
28
- this.user_count = 0;
29
- }
30
-
31
- /**
32
- * Helper to sum a specific field from an AggregatedPositions array.
33
- * @param {Array<object>} positions - The AggregatedPositions array.
34
- * @param {string} field - The field to sum ('Invested' or 'Value').
35
- * @returns {number} The total sum of that field.
36
- */
37
- _sumPositions(positions, field) {
38
- if (!positions || !Array.isArray(positions)) {
39
- return 0;
40
- }
41
- return positions.reduce((sum, pos) => sum + (pos[field] || 0), 0);
42
- }
43
-
44
- process(todayPortfolio, yesterdayPortfolio, userId) {
45
- // This calculation requires both days' data to compare
46
- if (!todayPortfolio || !yesterdayPortfolio || !todayPortfolio.AggregatedPositions || !yesterdayPortfolio.AggregatedPositions) {
47
- return;
48
- }
49
-
50
- const invested_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Invested');
51
- const value_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Value');
52
- const invested_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Invested');
53
- const value_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Value');
54
-
55
- // Only include users who have some form of positions
56
- if (invested_day1 === 0 && invested_day2 === 0 && value_day1 === 0 && value_day2 === 0) {
57
- return;
58
- }
59
-
60
- this.total_invested_day1 += invested_day1;
61
- this.total_value_day1 += value_day1;
62
- this.total_invested_day2 += invested_day2;
63
- this.total_value_day2 += value_day2;
64
- this.user_count++;
65
- }
66
-
67
- getResult() {
68
- if (this.user_count === 0) {
69
- return {}; // No users processed, return empty.
70
- }
71
-
72
- // 1. Calculate the average portfolio for the crowd
73
- const avg_invested_day1 = this.total_invested_day1 / this.user_count;
74
- const avg_value_day1 = this.total_value_day1 / this.user_count;
75
- const avg_invested_day2 = this.total_invested_day2 / this.user_count;
76
- const avg_value_day2 = this.total_value_day2 / this.user_count;
77
-
78
- // 2. Isolate the three effects
79
- const total_value_change = avg_value_day2 - avg_value_day1;
80
- const pl_effect = avg_value_day1 - avg_invested_day1;
81
- const trading_effect = avg_invested_day2 - avg_invested_day1;
82
-
83
- // 3. Solve for the Cash Flow Effect
84
- // Total_Change = pl_effect + trading_effect + cash_flow_effect
85
- const cash_flow_effect = total_value_change - pl_effect - trading_effect;
86
-
87
- return {
88
- // The final proxy value.
89
- // A negative value signals a net DEPOSIT.
90
- // A positive value signals a net WITHDRAWAL.
91
- cash_flow_effect_proxy: cash_flow_effect,
92
-
93
- // Interpretation for the frontend
94
- interpretation: "A negative value signals a net crowd deposit. A positive value signals a net crowd withdrawal.",
95
-
96
- // Debug components
97
- components: {
98
- total_value_change: total_value_change,
99
- pl_effect: pl_effect,
100
- trading_effect: trading_effect
101
- },
102
- // Debug averages
103
- averages: {
104
- avg_invested_day1: avg_invested_day1,
105
- avg_value_day1: avg_value_day1,
106
- avg_invested_day2: avg_invested_day2,
107
- avg_value_day2: avg_value_day2
108
- },
109
- user_sample_size: this.user_count
110
- };
111
- }
112
-
113
- reset() {
114
- this.total_invested_day1 = 0;
115
- this.total_value_day1 = 0;
116
- this.total_invested_day2 = 0;
117
- this.total_value_day2 = 0;
118
- this.user_count = 0;
119
- }
120
- }
121
-
1
+ /**
2
+ * @fileoverview Estimates a proxy for net crowd cash flow (Deposits vs. Withdrawals)
3
+ * by aggregating portfolio percentage changes across all users.
4
+ *
5
+ * This calculation is based on the formula:
6
+ * Total_Change = P/L_Effect + Trading_Effect + Cash_Flow_Effect
7
+ *
8
+ * Where:
9
+ * - Total_Change = avg_value_day2 - avg_value_day1
10
+ * - P/L_Effect = avg_value_day1 - avg_invested_day1
11
+ * - Trading_Effect = avg_invested_day2 - avg_invested_day1
12
+ *
13
+ * We solve for Cash_Flow_Effect, which serves as our proxy.
14
+ * A negative value indicates a net DEPOSIT (denominator grew, shrinking all %s).
15
+ * A positive value indicates a net WITHDRAWAL (denominator shrank, inflating all %s).
16
+ *
17
+ * NOTE: This file is a logical duplicate of deposit_withdrawal_percentage.js
18
+ * to satisfy the hardcoded 'crowd-cash-flow-proxy' dependency in several
19
+ * meta-calculations.
20
+ */
21
+
22
+ class CrowdCashFlowProxy {
23
+ constructor() {
24
+ this.total_invested_day1 = 0;
25
+ this.total_value_day1 = 0;
26
+ this.total_invested_day2 = 0;
27
+ this.total_value_day2 = 0;
28
+ this.user_count = 0;
29
+ }
30
+
31
+ /**
32
+ * Helper to sum a specific field from an AggregatedPositions array.
33
+ * @param {Array<object>} positions - The AggregatedPositions array.
34
+ * @param {string} field - The field to sum ('Invested' or 'Value').
35
+ * @returns {number} The total sum of that field.
36
+ */
37
+ _sumPositions(positions, field) {
38
+ if (!positions || !Array.isArray(positions)) {
39
+ return 0;
40
+ }
41
+ return positions.reduce((sum, pos) => sum + (pos[field] || 0), 0);
42
+ }
43
+
44
+ process(todayPortfolio, yesterdayPortfolio, userId) {
45
+ // This calculation requires both days' data to compare
46
+ if (!todayPortfolio || !yesterdayPortfolio || !todayPortfolio.AggregatedPositions || !yesterdayPortfolio.AggregatedPositions) {
47
+ return;
48
+ }
49
+
50
+ const invested_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Invested');
51
+ const value_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Value');
52
+ const invested_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Invested');
53
+ const value_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Value');
54
+
55
+ // Only include users who have some form of positions
56
+ if (invested_day1 === 0 && invested_day2 === 0 && value_day1 === 0 && value_day2 === 0) {
57
+ return;
58
+ }
59
+
60
+ this.total_invested_day1 += invested_day1;
61
+ this.total_value_day1 += value_day1;
62
+ this.total_invested_day2 += invested_day2;
63
+ this.total_value_day2 += value_day2;
64
+ this.user_count++;
65
+ }
66
+
67
+ getResult() {
68
+ if (this.user_count === 0) {
69
+ return {}; // No users processed, return empty.
70
+ }
71
+
72
+ // 1. Calculate the average portfolio for the crowd
73
+ const avg_invested_day1 = this.total_invested_day1 / this.user_count;
74
+ const avg_value_day1 = this.total_value_day1 / this.user_count;
75
+ const avg_invested_day2 = this.total_invested_day2 / this.user_count;
76
+ const avg_value_day2 = this.total_value_day2 / this.user_count;
77
+
78
+ // 2. Isolate the three effects
79
+ const total_value_change = avg_value_day2 - avg_value_day1;
80
+ const pl_effect = avg_value_day1 - avg_invested_day1;
81
+ const trading_effect = avg_invested_day2 - avg_invested_day1;
82
+
83
+ // 3. Solve for the Cash Flow Effect
84
+ // Total_Change = pl_effect + trading_effect + cash_flow_effect
85
+ const cash_flow_effect = total_value_change - pl_effect - trading_effect;
86
+
87
+ return {
88
+ // The final proxy value.
89
+ // A negative value signals a net DEPOSIT.
90
+ // A positive value signals a net WITHDRAWAL.
91
+ cash_flow_effect_proxy: cash_flow_effect,
92
+
93
+ // Interpretation for the frontend
94
+ interpretation: "A negative value signals a net crowd deposit. A positive value signals a net crowd withdrawal.",
95
+
96
+ // Debug components
97
+ components: {
98
+ total_value_change: total_value_change,
99
+ pl_effect: pl_effect,
100
+ trading_effect: trading_effect
101
+ },
102
+ // Debug averages
103
+ averages: {
104
+ avg_invested_day1: avg_invested_day1,
105
+ avg_value_day1: avg_value_day1,
106
+ avg_invested_day2: avg_invested_day2,
107
+ avg_value_day2: avg_value_day2
108
+ },
109
+ user_sample_size: this.user_count
110
+ };
111
+ }
112
+
113
+ reset() {
114
+ this.total_invested_day1 = 0;
115
+ this.total_value_day1 = 0;
116
+ this.total_invested_day2 = 0;
117
+ this.total_value_day2 = 0;
118
+ this.user_count = 0;
119
+ }
120
+ }
121
+
122
122
  module.exports = CrowdCashFlowProxy;
@@ -1,118 +1,118 @@
1
- /**
2
- * @fileoverview Estimates a proxy for net crowd cash flow (Deposits vs. Withdrawals)
3
- * by aggregating portfolio percentage changes across all users.
4
- *
5
- * This calculation is based on the formula:
6
- * Total_Change = P/L_Effect + Trading_Effect + Cash_Flow_Effect
7
- *
8
- * Where:
9
- * - Total_Change = avg_value_day2 - avg_value_day1
10
- * - P/L_Effect = avg_value_day1 - avg_invested_day1
11
- * - Trading_Effect = avg_invested_day2 - avg_invested_day1
12
- *
13
- * We solve for Cash_Flow_Effect, which serves as our proxy.
14
- * A negative value indicates a net DEPOSIT (denominator grew, shrinking all %s).
15
- * A positive value indicates a net WITHDRAWAL (denominator shrank, inflating all %s).
16
- */
17
-
18
- class CrowdCashFlowProxy {
19
- constructor() {
20
- this.total_invested_day1 = 0;
21
- this.total_value_day1 = 0;
22
- this.total_invested_day2 = 0;
23
- this.total_value_day2 = 0;
24
- this.user_count = 0;
25
- }
26
-
27
- /**
28
- * Helper to sum a specific field from an AggregatedPositions array.
29
- * @param {Array<object>} positions - The AggregatedPositions array.
30
- * @param {string} field - The field to sum ('Invested' or 'Value').
31
- * @returns {number} The total sum of that field.
32
- */
33
- _sumPositions(positions, field) {
34
- if (!positions || !Array.isArray(positions)) {
35
- return 0;
36
- }
37
- return positions.reduce((sum, pos) => sum + (pos[field] || 0), 0);
38
- }
39
-
40
- process(todayPortfolio, yesterdayPortfolio, userId) {
41
- // This calculation requires both days' data to compare
42
- if (!todayPortfolio || !yesterdayPortfolio || !todayPortfolio.AggregatedPositions || !yesterdayPortfolio.AggregatedPositions) {
43
- return;
44
- }
45
-
46
- const invested_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Invested');
47
- const value_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Value');
48
- const invested_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Invested');
49
- const value_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Value');
50
-
51
- // Only include users who have some form of positions
52
- if (invested_day1 === 0 && invested_day2 === 0 && value_day1 === 0 && value_day2 === 0) {
53
- return;
54
- }
55
-
56
- this.total_invested_day1 += invested_day1;
57
- this.total_value_day1 += value_day1;
58
- this.total_invested_day2 += invested_day2;
59
- this.total_value_day2 += value_day2;
60
- this.user_count++;
61
- }
62
-
63
- getResult() {
64
- if (this.user_count === 0) {
65
- return {}; // No users processed, return empty.
66
- }
67
-
68
- // 1. Calculate the average portfolio for the crowd
69
- const avg_invested_day1 = this.total_invested_day1 / this.user_count;
70
- const avg_value_day1 = this.total_value_day1 / this.user_count;
71
- const avg_invested_day2 = this.total_invested_day2 / this.user_count;
72
- const avg_value_day2 = this.total_value_day2 / this.user_count;
73
-
74
- // 2. Isolate the three effects
75
- const total_value_change = avg_value_day2 - avg_value_day1;
76
- const pl_effect = avg_value_day1 - avg_invested_day1;
77
- const trading_effect = avg_invested_day2 - avg_invested_day1;
78
-
79
- // 3. Solve for the Cash Flow Effect
80
- // Total_Change = pl_effect + trading_effect + cash_flow_effect
81
- const cash_flow_effect = total_value_change - pl_effect - trading_effect;
82
-
83
- return {
84
- // The final proxy value.
85
- // A negative value signals a net DEPOSIT.
86
- // A positive value signals a net WITHDRAWAL.
87
- cash_flow_effect_proxy: cash_flow_effect,
88
-
89
- // Interpretation for the frontend
90
- interpretation: "A negative value signals a net crowd deposit. A positive value signals a net crowd withdrawal.",
91
-
92
- // Debug components
93
- components: {
94
- total_value_change: total_value_change,
95
- pl_effect: pl_effect,
96
- trading_effect: trading_effect
97
- },
98
- // Debug averages
99
- averages: {
100
- avg_invested_day1: avg_invested_day1,
101
- avg_value_day1: avg_value_day1,
102
- avg_invested_day2: avg_invested_day2,
103
- avg_value_day2: avg_value_day2
104
- },
105
- user_sample_size: this.user_count
106
- };
107
- }
108
-
109
- reset() {
110
- this.total_invested_day1 = 0;
111
- this.total_value_day1 = 0;
112
- this.total_invested_day2 = 0;
113
- this.total_value_day2 = 0;
114
- this.user_count = 0;
115
- }
116
- }
117
-
1
+ /**
2
+ * @fileoverview Estimates a proxy for net crowd cash flow (Deposits vs. Withdrawals)
3
+ * by aggregating portfolio percentage changes across all users.
4
+ *
5
+ * This calculation is based on the formula:
6
+ * Total_Change = P/L_Effect + Trading_Effect + Cash_Flow_Effect
7
+ *
8
+ * Where:
9
+ * - Total_Change = avg_value_day2 - avg_value_day1
10
+ * - P/L_Effect = avg_value_day1 - avg_invested_day1
11
+ * - Trading_Effect = avg_invested_day2 - avg_invested_day1
12
+ *
13
+ * We solve for Cash_Flow_Effect, which serves as our proxy.
14
+ * A negative value indicates a net DEPOSIT (denominator grew, shrinking all %s).
15
+ * A positive value indicates a net WITHDRAWAL (denominator shrank, inflating all %s).
16
+ */
17
+
18
+ class CrowdCashFlowProxy {
19
+ constructor() {
20
+ this.total_invested_day1 = 0;
21
+ this.total_value_day1 = 0;
22
+ this.total_invested_day2 = 0;
23
+ this.total_value_day2 = 0;
24
+ this.user_count = 0;
25
+ }
26
+
27
+ /**
28
+ * Helper to sum a specific field from an AggregatedPositions array.
29
+ * @param {Array<object>} positions - The AggregatedPositions array.
30
+ * @param {string} field - The field to sum ('Invested' or 'Value').
31
+ * @returns {number} The total sum of that field.
32
+ */
33
+ _sumPositions(positions, field) {
34
+ if (!positions || !Array.isArray(positions)) {
35
+ return 0;
36
+ }
37
+ return positions.reduce((sum, pos) => sum + (pos[field] || 0), 0);
38
+ }
39
+
40
+ process(todayPortfolio, yesterdayPortfolio, userId) {
41
+ // This calculation requires both days' data to compare
42
+ if (!todayPortfolio || !yesterdayPortfolio || !todayPortfolio.AggregatedPositions || !yesterdayPortfolio.AggregatedPositions) {
43
+ return;
44
+ }
45
+
46
+ const invested_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Invested');
47
+ const value_day1 = this._sumPositions(yesterdayPortfolio.AggregatedPositions, 'Value');
48
+ const invested_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Invested');
49
+ const value_day2 = this._sumPositions(todayPortfolio.AggregatedPositions, 'Value');
50
+
51
+ // Only include users who have some form of positions
52
+ if (invested_day1 === 0 && invested_day2 === 0 && value_day1 === 0 && value_day2 === 0) {
53
+ return;
54
+ }
55
+
56
+ this.total_invested_day1 += invested_day1;
57
+ this.total_value_day1 += value_day1;
58
+ this.total_invested_day2 += invested_day2;
59
+ this.total_value_day2 += value_day2;
60
+ this.user_count++;
61
+ }
62
+
63
+ getResult() {
64
+ if (this.user_count === 0) {
65
+ return {}; // No users processed, return empty.
66
+ }
67
+
68
+ // 1. Calculate the average portfolio for the crowd
69
+ const avg_invested_day1 = this.total_invested_day1 / this.user_count;
70
+ const avg_value_day1 = this.total_value_day1 / this.user_count;
71
+ const avg_invested_day2 = this.total_invested_day2 / this.user_count;
72
+ const avg_value_day2 = this.total_value_day2 / this.user_count;
73
+
74
+ // 2. Isolate the three effects
75
+ const total_value_change = avg_value_day2 - avg_value_day1;
76
+ const pl_effect = avg_value_day1 - avg_invested_day1;
77
+ const trading_effect = avg_invested_day2 - avg_invested_day1;
78
+
79
+ // 3. Solve for the Cash Flow Effect
80
+ // Total_Change = pl_effect + trading_effect + cash_flow_effect
81
+ const cash_flow_effect = total_value_change - pl_effect - trading_effect;
82
+
83
+ return {
84
+ // The final proxy value.
85
+ // A negative value signals a net DEPOSIT.
86
+ // A positive value signals a net WITHDRAWAL.
87
+ cash_flow_effect_proxy: cash_flow_effect,
88
+
89
+ // Interpretation for the frontend
90
+ interpretation: "A negative value signals a net crowd deposit. A positive value signals a net crowd withdrawal.",
91
+
92
+ // Debug components
93
+ components: {
94
+ total_value_change: total_value_change,
95
+ pl_effect: pl_effect,
96
+ trading_effect: trading_effect
97
+ },
98
+ // Debug averages
99
+ averages: {
100
+ avg_invested_day1: avg_invested_day1,
101
+ avg_value_day1: avg_value_day1,
102
+ avg_invested_day2: avg_invested_day2,
103
+ avg_value_day2: avg_value_day2
104
+ },
105
+ user_sample_size: this.user_count
106
+ };
107
+ }
108
+
109
+ reset() {
110
+ this.total_invested_day1 = 0;
111
+ this.total_value_day1 = 0;
112
+ this.total_invested_day2 = 0;
113
+ this.total_value_day2 = 0;
114
+ this.user_count = 0;
115
+ }
116
+ }
117
+
118
118
  module.exports = CrowdCashFlowProxy;
@@ -1,49 +1,49 @@
1
- /**
2
- * @fileoverview Calculates the average percentage of total portfolio value
3
- * newly allocated to assets not held on the previous day.
4
- */
5
-
6
- class NewAllocationPercentage {
7
- constructor() {
8
- this.accumulatedNewAllocationPercentage = 0;
9
- this.userCount = 0;
10
- }
11
-
12
- process(todayPortfolio, yesterdayPortfolio, userId) {
13
- const todayPositions = todayPortfolio?.AggregatedPositions;
14
- const yesterdayPositions = yesterdayPortfolio?.AggregatedPositions || [];
15
-
16
- if (!todayPositions || todayPositions.length === 0) return;
17
-
18
- const yesterdayIds = new Set(yesterdayPositions.map(p => p.InstrumentID));
19
- let userNewAllocation = 0;
20
-
21
- for (const pos of todayPositions) {
22
- if (!yesterdayIds.has(pos.InstrumentID)) {
23
- const invested = typeof pos.Invested === 'number' ? pos.Invested : 0;
24
- userNewAllocation += invested;
25
- }
26
- }
27
-
28
- // Guard against data rounding or eToro API drift
29
- if (userNewAllocation > 100) userNewAllocation = 100;
30
-
31
- this.accumulatedNewAllocationPercentage += userNewAllocation;
32
- this.userCount++;
33
- }
34
-
35
- getResult() {
36
- if (this.userCount === 0) return {};
37
- return {
38
- average_new_allocation_percentage:
39
- this.accumulatedNewAllocationPercentage / this.userCount
40
- };
41
- }
42
-
43
- reset() {
44
- this.accumulatedNewAllocationPercentage = 0;
45
- this.userCount = 0;
46
- }
47
- }
48
-
49
- module.exports = NewAllocationPercentage;
1
+ /**
2
+ * @fileoverview Calculates the average percentage of total portfolio value
3
+ * newly allocated to assets not held on the previous day.
4
+ */
5
+
6
+ class NewAllocationPercentage {
7
+ constructor() {
8
+ this.accumulatedNewAllocationPercentage = 0;
9
+ this.userCount = 0;
10
+ }
11
+
12
+ process(todayPortfolio, yesterdayPortfolio, userId) {
13
+ const todayPositions = todayPortfolio?.AggregatedPositions;
14
+ const yesterdayPositions = yesterdayPortfolio?.AggregatedPositions || [];
15
+
16
+ if (!todayPositions || todayPositions.length === 0) return;
17
+
18
+ const yesterdayIds = new Set(yesterdayPositions.map(p => p.InstrumentID));
19
+ let userNewAllocation = 0;
20
+
21
+ for (const pos of todayPositions) {
22
+ if (!yesterdayIds.has(pos.InstrumentID)) {
23
+ const invested = typeof pos.Invested === 'number' ? pos.Invested : 0;
24
+ userNewAllocation += invested;
25
+ }
26
+ }
27
+
28
+ // Guard against data rounding or eToro API drift
29
+ if (userNewAllocation > 100) userNewAllocation = 100;
30
+
31
+ this.accumulatedNewAllocationPercentage += userNewAllocation;
32
+ this.userCount++;
33
+ }
34
+
35
+ getResult() {
36
+ if (this.userCount === 0) return {};
37
+ return {
38
+ average_new_allocation_percentage:
39
+ this.accumulatedNewAllocationPercentage / this.userCount
40
+ };
41
+ }
42
+
43
+ reset() {
44
+ this.accumulatedNewAllocationPercentage = 0;
45
+ this.userCount = 0;
46
+ }
47
+ }
48
+
49
+ module.exports = NewAllocationPercentage;