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,75 +1,75 @@
1
- /**
2
- * Compares the average P/L of speculator users who use TSL
3
- * vs. those who do not.
4
- */
5
- class TslEffectiveness {
6
- constructor() {
7
- this.tsl_group = { pnl_sum: 0, count: 0 };
8
- this.nontsl_group = { pnl_sum: 0, count: 0 };
9
- }
10
-
11
- /**
12
- * FIX: Helper function to calculate total P&L from positions
13
- * @param {object} portfolio
14
- * @returns {number|null}
15
- */
16
- _calculateTotalPnl(portfolio) {
17
- // Speculators use PublicPositions
18
- const positions = portfolio?.PublicPositions;
19
- if (positions && Array.isArray(positions)) {
20
- // Sum all NetProfit fields, defaulting to 0 if a position has no NetProfit
21
- return positions.reduce((sum, pos) => sum + (pos.NetProfit || 0), 0);
22
- }
23
- return null;
24
- }
25
-
26
- process(todayPortfolio, yesterdayPortfolio, userId) {
27
- // Check if user is a speculator and we have today's data
28
- // FIX: yesterdayPortfolio is not needed for this logic, only today's P&L
29
- if (todayPortfolio?.context?.userType !== 'speculator' || !todayPortfolio) {
30
- return;
31
- }
32
-
33
- const positions = todayPortfolio.PublicPositions;
34
-
35
- if (!positions || !Array.isArray(positions)) {
36
- return;
37
- }
38
-
39
- // FIX: Calculate dailyPnl by summing NetProfit from all positions
40
- const dailyPnl = this._calculateTotalPnl(todayPortfolio);
41
-
42
- if (dailyPnl === null) {
43
- return; // Cannot calculate P&L
44
- }
45
-
46
- const usesTSL = positions.some(p => p.IsTslEnabled);
47
-
48
- if (usesTSL) {
49
- this.tsl_group.pnl_sum += dailyPnl;
50
- this.tsl_group.count++;
51
- } else {
52
- this.nontsl_group.pnl_sum += dailyPnl;
53
- this.nontsl_group.count++;
54
- }
55
- }
56
-
57
- getResult() {
58
- // Return final calculated averages
59
- const tsl_avg_pnl = (this.tsl_group.count > 0) ? this.tsl_group.pnl_sum / this.tsl_group.count : 0;
60
- const nontsl_avg_pnl = (this.nontsl_group.count > 0) ? this.nontsl_group.pnl_sum / this.nontsl_group.count : 0;
61
-
62
- return {
63
- tsl_users: {
64
- avg_pnl: tsl_avg_pnl,
65
- count: this.tsl_group.count
66
- },
67
- nontsl_users: {
68
- avg_pnl: nontsl_avg_pnl,
69
- count: this.nontsl_group.count
70
- }
71
- };
72
- }
73
- }
74
-
1
+ /**
2
+ * Compares the average P/L of speculator users who use TSL
3
+ * vs. those who do not.
4
+ */
5
+ class TslEffectiveness {
6
+ constructor() {
7
+ this.tsl_group = { pnl_sum: 0, count: 0 };
8
+ this.nontsl_group = { pnl_sum: 0, count: 0 };
9
+ }
10
+
11
+ /**
12
+ * FIX: Helper function to calculate total P&L from positions
13
+ * @param {object} portfolio
14
+ * @returns {number|null}
15
+ */
16
+ _calculateTotalPnl(portfolio) {
17
+ // Speculators use PublicPositions
18
+ const positions = portfolio?.PublicPositions;
19
+ if (positions && Array.isArray(positions)) {
20
+ // Sum all NetProfit fields, defaulting to 0 if a position has no NetProfit
21
+ return positions.reduce((sum, pos) => sum + (pos.NetProfit || 0), 0);
22
+ }
23
+ return null;
24
+ }
25
+
26
+ process(todayPortfolio, yesterdayPortfolio, userId) {
27
+ // Check if user is a speculator and we have today's data
28
+ // FIX: yesterdayPortfolio is not needed for this logic, only today's P&L
29
+ if (todayPortfolio?.context?.userType !== 'speculator' || !todayPortfolio) {
30
+ return;
31
+ }
32
+
33
+ const positions = todayPortfolio.PublicPositions;
34
+
35
+ if (!positions || !Array.isArray(positions)) {
36
+ return;
37
+ }
38
+
39
+ // FIX: Calculate dailyPnl by summing NetProfit from all positions
40
+ const dailyPnl = this._calculateTotalPnl(todayPortfolio);
41
+
42
+ if (dailyPnl === null) {
43
+ return; // Cannot calculate P&L
44
+ }
45
+
46
+ const usesTSL = positions.some(p => p.IsTslEnabled);
47
+
48
+ if (usesTSL) {
49
+ this.tsl_group.pnl_sum += dailyPnl;
50
+ this.tsl_group.count++;
51
+ } else {
52
+ this.nontsl_group.pnl_sum += dailyPnl;
53
+ this.nontsl_group.count++;
54
+ }
55
+ }
56
+
57
+ getResult() {
58
+ // Return final calculated averages
59
+ const tsl_avg_pnl = (this.tsl_group.count > 0) ? this.tsl_group.pnl_sum / this.tsl_group.count : 0;
60
+ const nontsl_avg_pnl = (this.nontsl_group.count > 0) ? this.nontsl_group.pnl_sum / this.nontsl_group.count : 0;
61
+
62
+ return {
63
+ tsl_users: {
64
+ avg_pnl: tsl_avg_pnl,
65
+ count: this.tsl_group.count
66
+ },
67
+ nontsl_users: {
68
+ avg_pnl: nontsl_avg_pnl,
69
+ count: this.nontsl_group.count
70
+ }
71
+ };
72
+ }
73
+ }
74
+
75
75
  module.exports = TslEffectiveness;
package/index.js CHANGED
@@ -1,33 +1,33 @@
1
- /**
2
- * index.js
3
- * Main entry point for the Unified Calculations package.
4
- *
5
- * This file uses require-all to export all calculations dynamically
6
- * from their respective subdirectories. It also manually exports utility
7
- * functions for use within the calculations.
8
- */
9
-
10
- const requireAll = require('require-all');
11
- const path = require('path');
12
-
13
- // --- Utils (Manually Exported) ---
14
- const firestoreUtils = require('./utils/firestore_utils');
15
- const mappingProvider = require('./utils/sector_mapping_provider');
16
- const priceProvider = require('./utils/price_data_provider'); // <-- ADD THIS
17
-
18
- // --- Calculations (Dynamically Loaded) ---
19
- const calculations = requireAll({
20
- dirname: path.join(__dirname, 'calculations'), // Use __dirname to get the correct path
21
- filter: /(.+)\.js$/, // Only load .js files
22
- recursive: true, // Load from subdirectories
23
- map: name => name.replace(/_/g, '-') // Convert snake_case filenames to kebab-case keys
24
- });
25
-
26
- module.exports = {
27
- calculations,
28
- utils: {
29
- ...firestoreUtils,
30
- ...mappingProvider,
31
- ...priceProvider // <-- ADD THIS
32
- }
33
- };
1
+ /**
2
+ * index.js
3
+ * Main entry point for the Unified Calculations package.
4
+ *
5
+ * This file uses require-all to export all calculations dynamically
6
+ * from their respective subdirectories. It also manually exports utility
7
+ * functions for use within the calculations.
8
+ */
9
+
10
+ const requireAll = require('require-all');
11
+ const path = require('path');
12
+
13
+ // --- Utils (Manually Exported) ---
14
+ const firestoreUtils = require('./utils/firestore_utils');
15
+ const mappingProvider = require('./utils/sector_mapping_provider');
16
+ const priceProvider = require('./utils/price_data_provider'); // <-- ADD THIS
17
+
18
+ // --- Calculations (Dynamically Loaded) ---
19
+ const calculations = requireAll({
20
+ dirname: path.join(__dirname, 'calculations'), // Use __dirname to get the correct path
21
+ filter: /(.+)\.js$/, // Only load .js files
22
+ recursive: true, // Load from subdirectories
23
+ map: name => name.replace(/_/g, '-') // Convert snake_case filenames to kebab-case keys
24
+ });
25
+
26
+ module.exports = {
27
+ calculations,
28
+ utils: {
29
+ ...firestoreUtils,
30
+ ...mappingProvider,
31
+ ...priceProvider // <-- ADD THIS
32
+ }
33
+ };
package/package.json CHANGED
@@ -1,32 +1,32 @@
1
- {
2
- "name": "aiden-shared-calculations-unified",
3
- "version": "1.0.34",
4
- "description": "Shared calculation modules for the BullTrackers Computation System.",
5
- "main": "index.js",
6
- "files": [
7
- "index.js",
8
- "calculations/",
9
- "utils/"
10
- ],
11
- "scripts": {
12
- "test": "echo \"Error: no test specified\" && exit 1"
13
- },
14
- "keywords": [
15
- "bulltrackers",
16
- "etoro",
17
- "precompute",
18
- "calculations",
19
- "finance"
20
- ],
21
- "dependencies": {
22
- "@google-cloud/firestore": "^7.11.3",
23
- "sharedsetup": "latest",
24
- "require-all": "^3.0.0"
25
- },
26
- "engines": {
27
- "node": ">=20"
28
- },
29
- "publishConfig": {
30
- "access": "public"
31
- }
32
- }
1
+ {
2
+ "name": "aiden-shared-calculations-unified",
3
+ "version": "1.0.36",
4
+ "description": "Shared calculation modules for the BullTrackers Computation System.",
5
+ "main": "index.js",
6
+ "files": [
7
+ "index.js",
8
+ "calculations/",
9
+ "utils/"
10
+ ],
11
+ "scripts": {
12
+ "test": "echo \"Error: no test specified\" && exit 1"
13
+ },
14
+ "keywords": [
15
+ "bulltrackers",
16
+ "etoro",
17
+ "precompute",
18
+ "calculations",
19
+ "finance"
20
+ ],
21
+ "dependencies": {
22
+ "@google-cloud/firestore": "^7.11.3",
23
+ "sharedsetup": "latest",
24
+ "require-all": "^3.0.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ }
32
+ }
@@ -1,77 +1,77 @@
1
- /**
2
- * @fileoverview Provides a resilient retry wrapper for Firestore operations.
3
- */
4
- const { logger } = require("sharedsetup")(__filename); // Note: In the package, you might need to adjust how the logger is obtained if sharedsetup isn't a direct dependency or passed in.
5
-
6
- // Firestore error codes that are safe to retry
7
- const RETRYABLE_ERROR_CODES = new Set([
8
- 'DEADLINE_EXCEEDED',
9
- 'UNAVAILABLE',
10
- 'INTERNAL',
11
- 'RESOURCE_EXHAUSTED', // Can indicate contention
12
- ]);
13
-
14
- const MAX_RETRIES = 5;
15
- const INITIAL_BACKOFF_MS = 1000;
16
-
17
- /**
18
- * Delays execution for a specified number of milliseconds.
19
- * @param {number} ms The number of milliseconds to wait.
20
- * @returns {Promise<void>}
21
- */
22
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
23
-
24
- /**
25
- * Wraps an asynchronous Firestore operation with an exponential backoff retry mechanism.
26
- *
27
- * @param {() => Promise<T>} asyncOperation The async function to execute.
28
- * @param {string} operationName A descriptive name for the operation (for logging).
29
- * @returns {Promise<T>} The result of the async operation.
30
- * @template T
31
- */
32
- async function withRetry(asyncOperation, operationName = 'Firestore operation') {
33
- let lastError = null;
34
- let backoff = INITIAL_BACKOFF_MS;
35
-
36
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
37
- try {
38
- return await asyncOperation();
39
- } catch (error) {
40
- lastError = error;
41
- const code = error.code || error.details;
42
-
43
- if (RETRYABLE_ERROR_CODES.has(code)) {
44
- // Assuming logger is available globally or passed in somehow in the package context
45
- if (typeof logger !== 'undefined' && logger.log) {
46
- logger.log('WARN', `[Retry ${attempt}/${MAX_RETRIES}] Retrying ${operationName} due to ${code}. Waiting ${backoff}ms...`);
47
- } else {
48
- console.warn(`[Retry ${attempt}/${MAX_RETRIES}] Retrying ${operationName} due to ${code}. Waiting ${backoff}ms...`);
49
- }
50
- await sleep(backoff);
51
- backoff *= 2; // Exponential backoff
52
- } else {
53
- if (typeof logger !== 'undefined' && logger.log) {
54
- logger.log('ERROR', `[Retry] Non-retryable error during ${operationName}: ${code}`, { errorMessage: error.message });
55
- } else {
56
- console.error(`[Retry] Non-retryable error during ${operationName}: ${code}`, { errorMessage: error.message });
57
- }
58
- throw error; // Not a retryable error, re-throw immediately
59
- }
60
- }
61
- }
62
-
63
- if (typeof logger !== 'undefined' && logger.log) {
64
- logger.log('ERROR', `[Retry] ${operationName} failed after ${MAX_RETRIES} attempts.`, {
65
- finalErrorCode: lastError?.code || lastError?.details, // Added safe navigation
66
- errorMessage: lastError?.message // Added safe navigation
67
- });
68
- } else {
69
- console.error(`[Retry] ${operationName} failed after ${MAX_RETRIES} attempts.`, {
70
- finalErrorCode: lastError?.code || lastError?.details,
71
- errorMessage: lastError?.message
72
- });
73
- }
74
- throw lastError; // All retries failed, throw the last error
75
- }
76
-
1
+ /**
2
+ * @fileoverview Provides a resilient retry wrapper for Firestore operations.
3
+ */
4
+ const { logger } = require("sharedsetup")(__filename); // Note: In the package, you might need to adjust how the logger is obtained if sharedsetup isn't a direct dependency or passed in.
5
+
6
+ // Firestore error codes that are safe to retry
7
+ const RETRYABLE_ERROR_CODES = new Set([
8
+ 'DEADLINE_EXCEEDED',
9
+ 'UNAVAILABLE',
10
+ 'INTERNAL',
11
+ 'RESOURCE_EXHAUSTED', // Can indicate contention
12
+ ]);
13
+
14
+ const MAX_RETRIES = 5;
15
+ const INITIAL_BACKOFF_MS = 1000;
16
+
17
+ /**
18
+ * Delays execution for a specified number of milliseconds.
19
+ * @param {number} ms The number of milliseconds to wait.
20
+ * @returns {Promise<void>}
21
+ */
22
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
23
+
24
+ /**
25
+ * Wraps an asynchronous Firestore operation with an exponential backoff retry mechanism.
26
+ *
27
+ * @param {() => Promise<T>} asyncOperation The async function to execute.
28
+ * @param {string} operationName A descriptive name for the operation (for logging).
29
+ * @returns {Promise<T>} The result of the async operation.
30
+ * @template T
31
+ */
32
+ async function withRetry(asyncOperation, operationName = 'Firestore operation') {
33
+ let lastError = null;
34
+ let backoff = INITIAL_BACKOFF_MS;
35
+
36
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
37
+ try {
38
+ return await asyncOperation();
39
+ } catch (error) {
40
+ lastError = error;
41
+ const code = error.code || error.details;
42
+
43
+ if (RETRYABLE_ERROR_CODES.has(code)) {
44
+ // Assuming logger is available globally or passed in somehow in the package context
45
+ if (typeof logger !== 'undefined' && logger.log) {
46
+ logger.log('WARN', `[Retry ${attempt}/${MAX_RETRIES}] Retrying ${operationName} due to ${code}. Waiting ${backoff}ms...`);
47
+ } else {
48
+ console.warn(`[Retry ${attempt}/${MAX_RETRIES}] Retrying ${operationName} due to ${code}. Waiting ${backoff}ms...`);
49
+ }
50
+ await sleep(backoff);
51
+ backoff *= 2; // Exponential backoff
52
+ } else {
53
+ if (typeof logger !== 'undefined' && logger.log) {
54
+ logger.log('ERROR', `[Retry] Non-retryable error during ${operationName}: ${code}`, { errorMessage: error.message });
55
+ } else {
56
+ console.error(`[Retry] Non-retryable error during ${operationName}: ${code}`, { errorMessage: error.message });
57
+ }
58
+ throw error; // Not a retryable error, re-throw immediately
59
+ }
60
+ }
61
+ }
62
+
63
+ if (typeof logger !== 'undefined' && logger.log) {
64
+ logger.log('ERROR', `[Retry] ${operationName} failed after ${MAX_RETRIES} attempts.`, {
65
+ finalErrorCode: lastError?.code || lastError?.details, // Added safe navigation
66
+ errorMessage: lastError?.message // Added safe navigation
67
+ });
68
+ } else {
69
+ console.error(`[Retry] ${operationName} failed after ${MAX_RETRIES} attempts.`, {
70
+ finalErrorCode: lastError?.code || lastError?.details,
71
+ errorMessage: lastError?.message
72
+ });
73
+ }
74
+ throw lastError; // All retries failed, throw the last error
75
+ }
76
+
77
77
  module.exports = { withRetry, sleep };