aiden-shared-calculations-unified 1.0.34 → 1.0.36

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 -170
  9. package/calculations/behavioural/historical/drawdown_response.js +58 -58
  10. package/calculations/behavioural/historical/dumb-cohort-flow.js +249 -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 +250 -250
  17. package/calculations/behavioural/historical/smart_money_flow.js +165 -165
  18. package/calculations/behavioural/historical/user-investment-profile.js +412 -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,166 +1,166 @@
1
- /**
2
- * @fileoverview Tracks the investment flow of "smart money".
3
- */
4
-
5
- const { Firestore } = require('@google-cloud/firestore');
6
- const firestore = new Firestore();
7
- // CORRECTED PATH: ../utils/ instead of ../../utils/
8
- const { getInstrumentSectorMap } = require('../../../utils/sector_mapping_provider');
9
-
10
- class SmartMoneyFlow {
11
- // ... (rest of the code is unchanged) ...
12
- constructor() {
13
- this.smartMoneyUsers = new Set();
14
- this.sectorFlow = {};
15
- this.sectorMap = null; // Cache for the sector map
16
- }
17
-
18
- async process(todayPortfolio, yesterdayPortfolio, userId) {
19
- // Load smart money users only if the set is empty
20
- if (this.smartMoneyUsers.size === 0) {
21
- await this.identifySmartMoney();
22
- // Log after identification attempt
23
- console.log(`Identified ${this.smartMoneyUsers.size} smart money users.`);
24
- }
25
-
26
- // Load sector map if not already loaded
27
- if (!this.sectorMap) {
28
- try {
29
- this.sectorMap = await getInstrumentSectorMap();
30
- if (!this.sectorMap || Object.keys(this.sectorMap).length === 0) {
31
- console.warn('Sector map loaded but is empty.');
32
- } else {
33
- // console.log('Sector map loaded successfully.'); // Optional: remove if too verbose
34
- }
35
- } catch (error) {
36
- console.error('Failed to load sector map:', error);
37
- return; // Stop processing if sector map fails to load
38
- }
39
- }
40
-
41
- // Check if the current user is considered "smart money"
42
- if (this.smartMoneyUsers.has(userId) && todayPortfolio && yesterdayPortfolio) {
43
- // Ensure sectorMap is available before proceeding
44
- if (!this.sectorMap) {
45
- console.warn(`Skipping user ${userId}: Sector map not available.`);
46
- return;
47
- }
48
- const todaySectorInvestment = this.calculateSectorInvestment(todayPortfolio);
49
- const yesterdaySectorInvestment = this.calculateSectorInvestment(yesterdayPortfolio);
50
-
51
- // Calculate change in investment per sector
52
- const allSectors = new Set([...Object.keys(todaySectorInvestment), ...Object.keys(yesterdaySectorInvestment)]);
53
- for (const sector of allSectors) {
54
- const todayAmount = todaySectorInvestment[sector] || 0;
55
- const yesterdayAmount = yesterdaySectorInvestment[sector] || 0;
56
- const change = todayAmount - yesterdayAmount;
57
-
58
- // Only record if there is a change
59
- if (change !== 0) {
60
- if (!this.sectorFlow[sector]) {
61
- this.sectorFlow[sector] = 0;
62
- }
63
- this.sectorFlow[sector] += change;
64
- }
65
- }
66
- } else {
67
- // Optional: Log why processing is skipped for a user
68
- // if (!this.smartMoneyUsers.has(userId)) console.log(`User ${userId} is not smart money.`);
69
- // if (!todayPortfolio) console.log(`User ${userId}: Missing today's portfolio.`);
70
- // if (!yesterdayPortfolio) console.log(`User ${userId}: Missing yesterday's portfolio.`);
71
- }
72
- }
73
-
74
- async identifySmartMoney() {
75
- console.log("Attempting to identify smart money users...");
76
- try {
77
- // Fetching sharded profitability data
78
- const shardPromises = [];
79
- const NUM_SHARDS = 50; // Ensure this matches the value used in user_profitability_tracker
80
- for (let i = 0; i < NUM_SHARDS; i++) {
81
- // Corrected document path for shards
82
- const docRef = firestore.collection('historical_insights').doc(`user_profitability_shard_${i}`);
83
- shardPromises.push(docRef.get());
84
- }
85
- const shardSnapshots = await Promise.all(shardPromises);
86
-
87
- let profitableUsersFound = 0;
88
- shardSnapshots.forEach((snap, index) => {
89
- if (snap.exists) {
90
- const shardData = snap.data().profits || {}; // Assuming data structure { profits: { userId: [...] } }
91
- for (const userId in shardData) {
92
- const history = shardData[userId];
93
- // Check if history is an array and has enough entries
94
- if (Array.isArray(history) && history.length >= 5) {
95
- // "Smart money" definition: profitable for at least 5 of the last 7 days
96
- // Filter based on the 'pnl' property of each history entry
97
- const profitableDays = history.slice(-7).filter(d => d && typeof d.pnl === 'number' && d.pnl > 0).length;
98
- if (profitableDays >= 5) {
99
- this.smartMoneyUsers.add(userId);
100
- profitableUsersFound++;
101
- }
102
- }
103
- }
104
- } else {
105
- console.warn(`Profitability shard user_profitability_shard_${index} does not exist.`);
106
- }
107
- });
108
- console.log(`Found ${profitableUsersFound} potentially smart money users across all shards.`);
109
-
110
- } catch (error) {
111
- console.error('Error identifying smart money users:', error);
112
- // Decide how to handle error: maybe retry, or proceed without smart money data
113
- this.smartMoneyUsers.clear(); // Ensure set is empty on error
114
- }
115
- // Final check after attempt
116
- if (this.smartMoneyUsers.size === 0) {
117
- console.warn("No smart money users identified. Smart money flow calculation might be empty.");
118
- }
119
- }
120
-
121
-
122
- calculateSectorInvestment(portfolio) {
123
- const sectorInvestment = {};
124
- // Ensure sectorMap is loaded
125
- if (!this.sectorMap) {
126
- console.warn("Cannot calculate sector investment: Sector map not loaded.");
127
- return sectorInvestment; // Return empty object
128
- }
129
-
130
- // Use AggregatedPositions as it contains 'Invested' amount
131
- if (portfolio && portfolio.AggregatedPositions && Array.isArray(portfolio.AggregatedPositions)) {
132
- for (const pos of portfolio.AggregatedPositions) {
133
- if (pos && typeof pos.InstrumentID !== 'undefined' && typeof pos.Invested === 'number') {
134
- const sector = this.sectorMap[pos.InstrumentID] || 'N/A';
135
- if (!sectorInvestment[sector]) {
136
- sectorInvestment[sector] = 0;
137
- }
138
- sectorInvestment[sector] += pos.Invested;
139
- } else {
140
- // Log potentially malformed position data
141
- // console.warn('Skipping position due to missing InstrumentID or Invested amount:', pos);
142
- }
143
- }
144
- } else {
145
- // Log if AggregatedPositions are missing or not an array
146
- // console.warn('AggregatedPositions missing or invalid in portfolio for sector investment calculation.');
147
- }
148
- return sectorInvestment;
149
- }
150
-
151
-
152
- getResult() {
153
- // Return only sectors with non-zero flow if desired, or the full object
154
- const filteredFlow = {};
155
- for (const sector in this.sectorFlow) {
156
- if (this.sectorFlow[sector] !== 0) {
157
- filteredFlow[sector] = this.sectorFlow[sector];
158
- }
159
- }
160
- // console.log("Final Smart Money Flow:", filteredFlow); // Optional: Log final result
161
- return { smart_money_flow: filteredFlow };
162
- // Or return the full object: return { smart_money_flow: this.sectorFlow };
163
- }
164
- }
165
-
1
+ /**
2
+ * @fileoverview Tracks the investment flow of "smart money".
3
+ */
4
+
5
+ const { Firestore } = require('@google-cloud/firestore');
6
+ const firestore = new Firestore();
7
+ // CORRECTED PATH: ../utils/ instead of ../../utils/
8
+ const { getInstrumentSectorMap } = require('../../../utils/sector_mapping_provider');
9
+
10
+ class SmartMoneyFlow {
11
+ // ... (rest of the code is unchanged) ...
12
+ constructor() {
13
+ this.smartMoneyUsers = new Set();
14
+ this.sectorFlow = {};
15
+ this.sectorMap = null; // Cache for the sector map
16
+ }
17
+
18
+ async process(todayPortfolio, yesterdayPortfolio, userId) {
19
+ // Load smart money users only if the set is empty
20
+ if (this.smartMoneyUsers.size === 0) {
21
+ await this.identifySmartMoney();
22
+ // Log after identification attempt
23
+ console.log(`Identified ${this.smartMoneyUsers.size} smart money users.`);
24
+ }
25
+
26
+ // Load sector map if not already loaded
27
+ if (!this.sectorMap) {
28
+ try {
29
+ this.sectorMap = await getInstrumentSectorMap();
30
+ if (!this.sectorMap || Object.keys(this.sectorMap).length === 0) {
31
+ console.warn('Sector map loaded but is empty.');
32
+ } else {
33
+ // console.log('Sector map loaded successfully.'); // Optional: remove if too verbose
34
+ }
35
+ } catch (error) {
36
+ console.error('Failed to load sector map:', error);
37
+ return; // Stop processing if sector map fails to load
38
+ }
39
+ }
40
+
41
+ // Check if the current user is considered "smart money"
42
+ if (this.smartMoneyUsers.has(userId) && todayPortfolio && yesterdayPortfolio) {
43
+ // Ensure sectorMap is available before proceeding
44
+ if (!this.sectorMap) {
45
+ console.warn(`Skipping user ${userId}: Sector map not available.`);
46
+ return;
47
+ }
48
+ const todaySectorInvestment = this.calculateSectorInvestment(todayPortfolio);
49
+ const yesterdaySectorInvestment = this.calculateSectorInvestment(yesterdayPortfolio);
50
+
51
+ // Calculate change in investment per sector
52
+ const allSectors = new Set([...Object.keys(todaySectorInvestment), ...Object.keys(yesterdaySectorInvestment)]);
53
+ for (const sector of allSectors) {
54
+ const todayAmount = todaySectorInvestment[sector] || 0;
55
+ const yesterdayAmount = yesterdaySectorInvestment[sector] || 0;
56
+ const change = todayAmount - yesterdayAmount;
57
+
58
+ // Only record if there is a change
59
+ if (change !== 0) {
60
+ if (!this.sectorFlow[sector]) {
61
+ this.sectorFlow[sector] = 0;
62
+ }
63
+ this.sectorFlow[sector] += change;
64
+ }
65
+ }
66
+ } else {
67
+ // Optional: Log why processing is skipped for a user
68
+ // if (!this.smartMoneyUsers.has(userId)) console.log(`User ${userId} is not smart money.`);
69
+ // if (!todayPortfolio) console.log(`User ${userId}: Missing today's portfolio.`);
70
+ // if (!yesterdayPortfolio) console.log(`User ${userId}: Missing yesterday's portfolio.`);
71
+ }
72
+ }
73
+
74
+ async identifySmartMoney() {
75
+ console.log("Attempting to identify smart money users...");
76
+ try {
77
+ // Fetching sharded profitability data
78
+ const shardPromises = [];
79
+ const NUM_SHARDS = 50; // Ensure this matches the value used in user_profitability_tracker
80
+ for (let i = 0; i < NUM_SHARDS; i++) {
81
+ // Corrected document path for shards
82
+ const docRef = firestore.collection('historical_insights').doc(`user_profitability_shard_${i}`);
83
+ shardPromises.push(docRef.get());
84
+ }
85
+ const shardSnapshots = await Promise.all(shardPromises);
86
+
87
+ let profitableUsersFound = 0;
88
+ shardSnapshots.forEach((snap, index) => {
89
+ if (snap.exists) {
90
+ const shardData = snap.data().profits || {}; // Assuming data structure { profits: { userId: [...] } }
91
+ for (const userId in shardData) {
92
+ const history = shardData[userId];
93
+ // Check if history is an array and has enough entries
94
+ if (Array.isArray(history) && history.length >= 5) {
95
+ // "Smart money" definition: profitable for at least 5 of the last 7 days
96
+ // Filter based on the 'pnl' property of each history entry
97
+ const profitableDays = history.slice(-7).filter(d => d && typeof d.pnl === 'number' && d.pnl > 0).length;
98
+ if (profitableDays >= 5) {
99
+ this.smartMoneyUsers.add(userId);
100
+ profitableUsersFound++;
101
+ }
102
+ }
103
+ }
104
+ } else {
105
+ console.warn(`Profitability shard user_profitability_shard_${index} does not exist.`);
106
+ }
107
+ });
108
+ console.log(`Found ${profitableUsersFound} potentially smart money users across all shards.`);
109
+
110
+ } catch (error) {
111
+ console.error('Error identifying smart money users:', error);
112
+ // Decide how to handle error: maybe retry, or proceed without smart money data
113
+ this.smartMoneyUsers.clear(); // Ensure set is empty on error
114
+ }
115
+ // Final check after attempt
116
+ if (this.smartMoneyUsers.size === 0) {
117
+ console.warn("No smart money users identified. Smart money flow calculation might be empty.");
118
+ }
119
+ }
120
+
121
+
122
+ calculateSectorInvestment(portfolio) {
123
+ const sectorInvestment = {};
124
+ // Ensure sectorMap is loaded
125
+ if (!this.sectorMap) {
126
+ console.warn("Cannot calculate sector investment: Sector map not loaded.");
127
+ return sectorInvestment; // Return empty object
128
+ }
129
+
130
+ // Use AggregatedPositions as it contains 'Invested' amount
131
+ if (portfolio && portfolio.AggregatedPositions && Array.isArray(portfolio.AggregatedPositions)) {
132
+ for (const pos of portfolio.AggregatedPositions) {
133
+ if (pos && typeof pos.InstrumentID !== 'undefined' && typeof pos.Invested === 'number') {
134
+ const sector = this.sectorMap[pos.InstrumentID] || 'N/A';
135
+ if (!sectorInvestment[sector]) {
136
+ sectorInvestment[sector] = 0;
137
+ }
138
+ sectorInvestment[sector] += pos.Invested;
139
+ } else {
140
+ // Log potentially malformed position data
141
+ // console.warn('Skipping position due to missing InstrumentID or Invested amount:', pos);
142
+ }
143
+ }
144
+ } else {
145
+ // Log if AggregatedPositions are missing or not an array
146
+ // console.warn('AggregatedPositions missing or invalid in portfolio for sector investment calculation.');
147
+ }
148
+ return sectorInvestment;
149
+ }
150
+
151
+
152
+ getResult() {
153
+ // Return only sectors with non-zero flow if desired, or the full object
154
+ const filteredFlow = {};
155
+ for (const sector in this.sectorFlow) {
156
+ if (this.sectorFlow[sector] !== 0) {
157
+ filteredFlow[sector] = this.sectorFlow[sector];
158
+ }
159
+ }
160
+ // console.log("Final Smart Money Flow:", filteredFlow); // Optional: Log final result
161
+ return { smart_money_flow: filteredFlow };
162
+ // Or return the full object: return { smart_money_flow: this.sectorFlow };
163
+ }
164
+ }
165
+
166
166
  module.exports = SmartMoneyFlow;