aiden-shared-calculations-unified 1.0.95 → 1.0.96

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 (54) hide show
  1. package/calculations/capitulation/asset-volatility-estimator.js +1 -2
  2. package/calculations/capitulation/retail-capitulation-risk-forecast.js +1 -2
  3. package/calculations/ghost-book/cost-basis-density.js +1 -2
  4. package/calculations/ghost-book/liquidity-vacuum.js +1 -2
  5. package/calculations/ghost-book/retail-gamma-exposure.js +0 -1
  6. package/calculations/predicative-alpha/cognitive-dissonance.js +1 -2
  7. package/calculations/predicative-alpha/diamond-hand-fracture.js +1 -2
  8. package/calculations/predicative-alpha/mimetic-latency.js +1 -2
  9. package/package.json +1 -1
  10. package/calculations/legacy/activity_by_pnl_status.js +0 -119
  11. package/calculations/legacy/asset_crowd_flow.js +0 -163
  12. package/calculations/legacy/capital_deployment_strategy.js +0 -108
  13. package/calculations/legacy/capital_liquidation_performance.js +0 -139
  14. package/calculations/legacy/capital_vintage_performance.js +0 -136
  15. package/calculations/legacy/cash-flow-deployment.js +0 -144
  16. package/calculations/legacy/cash-flow-liquidation.js +0 -146
  17. package/calculations/legacy/crowd-cash-flow-proxy.js +0 -128
  18. package/calculations/legacy/crowd_conviction_score.js +0 -261
  19. package/calculations/legacy/crowd_sharpe_ratio_proxy.js +0 -137
  20. package/calculations/legacy/daily_asset_activity.js +0 -128
  21. package/calculations/legacy/daily_user_activity_tracker.js +0 -182
  22. package/calculations/legacy/deposit_withdrawal_percentage.js +0 -125
  23. package/calculations/legacy/diversification_pnl.js +0 -115
  24. package/calculations/legacy/drawdown_response.js +0 -137
  25. package/calculations/legacy/dumb-cohort-flow.js +0 -238
  26. package/calculations/legacy/gain_response.js +0 -137
  27. package/calculations/legacy/historical_performance_aggregator.js +0 -85
  28. package/calculations/legacy/in_loss_asset_crowd_flow.js +0 -168
  29. package/calculations/legacy/in_profit_asset_crowd_flow.js +0 -168
  30. package/calculations/legacy/negative_expectancy_cohort_flow.js +0 -232
  31. package/calculations/legacy/new_allocation_percentage.js +0 -98
  32. package/calculations/legacy/paper_vs_diamond_hands.js +0 -107
  33. package/calculations/legacy/position_count_pnl.js +0 -120
  34. package/calculations/legacy/positive_expectancy_cohort_flow.js +0 -232
  35. package/calculations/legacy/profit_cohort_divergence.js +0 -115
  36. package/calculations/legacy/profitability_migration.js +0 -104
  37. package/calculations/legacy/reallocation_increase_percentage.js +0 -104
  38. package/calculations/legacy/risk_appetite_change.js +0 -97
  39. package/calculations/legacy/sector_rotation.js +0 -117
  40. package/calculations/legacy/shark_attack_signal.js +0 -112
  41. package/calculations/legacy/smart-cohort-flow.js +0 -238
  42. package/calculations/legacy/smart-dumb-divergence-index.js +0 -143
  43. package/calculations/legacy/smart_dumb_divergence_index_v2.js +0 -138
  44. package/calculations/legacy/smart_money_flow.js +0 -198
  45. package/calculations/legacy/social-predictive-regime-state.js +0 -102
  46. package/calculations/legacy/social-topic-driver-index.js +0 -147
  47. package/calculations/legacy/social-topic-predictive-potential.js +0 -461
  48. package/calculations/legacy/social_flow_correlation.js +0 -112
  49. package/calculations/legacy/speculator_adjustment_activity.js +0 -103
  50. package/calculations/legacy/strategy-performance.js +0 -265
  51. package/calculations/legacy/tsl_effectiveness.js +0 -85
  52. package/calculations/legacy/user-investment-profile.js +0 -313
  53. package/calculations/legacy/user_expectancy_score.js +0 -106
  54. package/calculations/legacy/user_profitability_tracker.js +0 -131
@@ -1,182 +0,0 @@
1
- /**
2
- * @fileoverview Tracks user activity by comparing portfolio snapshots.
3
- * This is a historical calculation that defines an "active user" as someone
4
- * who has opened, closed, or reallocated a position within the last 24 hours.
5
- *
6
- * This provides the "Daily Active Users" count for the monitored cohort.
7
- * This depreciates the user activity sampler cloud function which was inefficient for the api usage and now provides this data for free
8
- */
9
-
10
- class DailyUserActivityTracker {
11
- constructor() {
12
- this.activeUserIds = new Set();
13
- this.activityEvents = {
14
- new_position: 0,
15
- closed_position: 0,
16
- reallocation: 0
17
- };
18
- }
19
-
20
- /**
21
- * Defines the output schema for this calculation.
22
- * @returns {object} JSON Schema object
23
- */
24
- static getSchema() {
25
- return {
26
- "type": "object",
27
- "description": "Tracks the count of daily active users based on portfolio changes.",
28
- "properties": {
29
- "rawActiveUserCount": {
30
- "type": "number",
31
- "description": "The total count of unique users who were active today (opened, closed, or reallocated)."
32
- },
33
- "activityBreakdown": {
34
- "type": "object",
35
- "description": "A breakdown of the first activity event type recorded for active users.",
36
- "properties": {
37
- "new_position": {
38
- "type": "number",
39
- "description": "Count of users whose first detected activity was opening a new position."
40
- },
41
- "closed_position": {
42
- "type": "number",
43
- "description": "Count of users whose first detected activity was closing an existing position."
44
- },
45
- "reallocation": {
46
- "type": "number",
47
- "description": "Count of users whose first detected activity was reallocating an existing position."
48
- }
49
- },
50
- "required": ["new_position", "closed_position", "reallocation"]
51
- }
52
- },
53
- "required": ["rawActiveUserCount", "activityBreakdown"]
54
- };
55
- }
56
-
57
- /**
58
- * Helper to get a simplified map of positions for comparison.
59
- * @param {object} portfolio - A user's full portfolio object.
60
- * @returns {object} { posMap: Map<InstrumentID, {invested: number}>, hasAggregated: boolean }
61
- */
62
- _getPortfolioMaps(portfolio) {
63
- // Prioritize AggregatedPositions, but fall back to PublicPositions
64
- const positions = portfolio?.AggregatedPositions || portfolio?.PublicPositions;
65
- if (!positions || !Array.isArray(positions)) {
66
- return { posMap: new Map(), hasAggregated: false };
67
- }
68
-
69
- const posMap = new Map();
70
- for (const pos of positions) {
71
- const key = pos.InstrumentID;
72
- if (key) {
73
- posMap.set(key, {
74
- // 'InvestedAmount' or 'Invested' is the portfolio percentage
75
- // We use this for reallocation logic.
76
- invested: pos.InvestedAmount || pos.Invested || pos.Amount || 0
77
- });
78
- }
79
- }
80
- // Return the map and a flag indicating if we can trust the 'invested' field
81
- return { posMap, hasAggregated: !!portfolio.AggregatedPositions };
82
- }
83
-
84
- /**
85
- * Processes a single user's daily data.
86
- */
87
- process(todayPortfolio, yesterdayPortfolio, userId) {
88
- // This calculation requires both days to find changes.
89
- if (!todayPortfolio || !yesterdayPortfolio) {
90
- return;
91
- }
92
-
93
- const { posMap: yPosMap, hasAggregated: yHasAgg } = this._getPortfolioMaps(yesterdayPortfolio);
94
- const { posMap: tPosMap, hasAggregated: tHasAgg } = this._getPortfolioMaps(todayPortfolio);
95
-
96
- // Skip if user has no positions on either day
97
- if (tPosMap.size === 0 && yPosMap.size === 0) {
98
- return;
99
- }
100
-
101
- const yIds = new Set(yPosMap.keys());
102
- const tIds = new Set(tPosMap.keys());
103
- let isActive = false;
104
-
105
- // 1. Check for new positions (high-confidence activity)
106
- for (const tId of tIds) {
107
- if (!yIds.has(tId)) {
108
- isActive = true;
109
- this.activityEvents.new_position++;
110
- break; // Found activity, no need to check more
111
- }
112
- }
113
-
114
- if (isActive) {
115
- this.activeUserIds.add(userId);
116
- return;
117
- }
118
-
119
- // 2. Check for closed positions (high-confidence activity)
120
- for (const yId of yIds) {
121
- if (!tIds.has(yId)) {
122
- isActive = true;
123
- this.activityEvents.closed_position++;
124
- break; // Found activity
125
- }
126
- }
127
-
128
- if (isActive) {
129
- this.activeUserIds.add(userId);
130
- return;
131
- }
132
-
133
- // 3. Check for reallocation (only possible if we have AggregatedPositions for both days)
134
- // This checks for changes in the 'Invested' percentage
135
- // 3. Check for reallocation (only possible if we have AggregatedPositions for both days)
136
- if (yHasAgg && tHasAgg) {
137
- for (const tId of tIds) {
138
- const tInvested = tPosMap.get(tId).invested;
139
- const yInvested = yPosMap.get(tId)?.invested ?? 0;
140
-
141
- // Check for a meaningful change (e.g., > 0.01% to avoid float noise)
142
- if (Math.abs(tInvested - yInvested) > 0.0001) {
143
- isActive = true;
144
- this.activityEvents.reallocation++;
145
- break;
146
- }
147
- }
148
- }
149
-
150
-
151
- if (isActive) {
152
- this.activeUserIds.add(userId);
153
- }
154
- }
155
-
156
- /**
157
- * Returns the final aggregated counts for the day.
158
- */
159
- getResult() {
160
- return {
161
- // This is the main metric for your graph
162
- rawActiveUserCount: this.activeUserIds.size,
163
-
164
- // This is a bonus metric to see *what* users are doing
165
- activityBreakdown: this.activityEvents
166
- };
167
- }
168
-
169
- /**
170
- * Resets the counters for the next run.
171
- */
172
- reset() {
173
- this.activeUserIds.clear();
174
- this.activityEvents = {
175
- new_position: 0,
176
- closed_position: 0,
177
- reallocation: 0
178
- };
179
- }
180
- }
181
-
182
- module.exports = DailyUserActivityTracker;
@@ -1,125 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 2) for deposit/withdrawal percentage.
3
- *
4
- * This metric estimates the net cash flow (deposits vs. withdrawals)
5
- * by analyzing changes in portfolio percentages.
6
- *
7
- * It's a proxy based on the assumption that changes in the *sum* of
8
- * position percentages not accounted for by P&L represent new cash.
9
- *
10
- * (DEPRECIATED by crowd-cash-flow-proxy, but kept for reference)
11
- */
12
- class DepositWithdrawalPercentage {
13
- constructor() {
14
- this.invested_pct_yesterday_sum = 0;
15
- this.invested_pct_today_sum = 0;
16
- this.pnl_pct_today_sum = 0;
17
- this.user_count = 0;
18
- }
19
-
20
- /**
21
- * Defines the output schema for this calculation.
22
- * @returns {object} JSON Schema object
23
- */
24
- static getSchema() {
25
- return {
26
- "type": "object",
27
- "description": "(DEPRECIATED) Estimates net cash flow by analyzing changes in portfolio position percentages.",
28
- "properties": {
29
- "estimated_net_flow_pct": {
30
- "type": "number",
31
- "description": "The estimated net cash flow (deposit/withdrawal) as an average percentage change across all users."
32
- },
33
- "user_count": {
34
- "type": "number",
35
- "description": "Number of users processed."
36
- },
37
- "debug": {
38
- "type": "object",
39
- "properties": {
40
- "invested_pct_yesterday_sum": { "type": "number" },
41
- "invested_pct_today_sum": { "type": "number" },
42
- "pnl_pct_today_sum": { "type": "number" }
43
- }
44
- }
45
- },
46
- "required": ["estimated_net_flow_pct", "user_count"]
47
- };
48
- }
49
-
50
- _getPortfolioSums(portfolio) {
51
- const positions = portfolio?.AggregatedPositions;
52
- if (!positions || !Array.isArray(positions)) {
53
- return { investedPct: 0, pnlPct: 0 };
54
- }
55
-
56
- let investedPct = 0;
57
- let pnlPct = 0;
58
-
59
- for (const pos of positions) {
60
- investedPct += pos.InvestedAmount || pos.Invested || 0;
61
- pnlPct += (pos.NetProfit || 0) / 100; // Assuming NetProfit is in %
62
- }
63
-
64
- return { investedPct, pnlPct };
65
- }
66
-
67
- process(todayPortfolio, yesterdayPortfolio) {
68
- if (!todayPortfolio || !yesterdayPortfolio) {
69
- return;
70
- }
71
-
72
- const ySums = this._getPortfolioSums(yesterdayPortfolio);
73
- const tSums = this._getPortfolioSums(todayPortfolio);
74
-
75
- if (ySums.investedPct === 0 && tSums.investedPct === 0) {
76
- return; // Skip users with no positions
77
- }
78
-
79
- this.invested_pct_yesterday_sum += ySums.investedPct;
80
- this.invested_pct_today_sum += tSums.investedPct;
81
- this.pnl_pct_today_sum += tSums.pnlPct; // Use today's P&L
82
- this.user_count++;
83
- }
84
-
85
- getResult() {
86
- if (this.user_count === 0) {
87
- return {
88
- estimated_net_flow_pct: 0,
89
- user_count: 0,
90
- debug: {
91
- invested_pct_yesterday_sum: 0,
92
- invested_pct_today_sum: 0,
93
- pnl_pct_today_sum: 0
94
- }
95
- };
96
- }
97
-
98
- // Avg % change in invested capital
99
- const avg_invested_change = (this.invested_pct_today_sum - this.invested_pct_yesterday_sum) / this.user_count;
100
- // Avg P&L
101
- const avg_pnl = this.pnl_pct_today_sum / this.user_count;
102
-
103
- // The change not accounted for by P&L is the proxy flow
104
- const net_flow_pct = avg_invested_change - avg_pnl;
105
-
106
- return {
107
- estimated_net_flow_pct: net_flow_pct,
108
- user_count: this.user_count,
109
- debug: {
110
- invested_pct_yesterday_sum: this.invested_pct_yesterday_sum,
111
- invested_pct_today_sum: this.invested_pct_today_sum,
112
- pnl_pct_today_sum: this.pnl_pct_today_sum
113
- }
114
- };
115
- }
116
-
117
- reset() {
118
- this.invested_pct_yesterday_sum = 0;
119
- this.invested_pct_today_sum = 0;
120
- this.pnl_pct_today_sum = 0;
121
- this.user_count = 0;
122
- }
123
- }
124
-
125
- module.exports = DepositWithdrawalPercentage;
@@ -1,115 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 1) for Diversification vs. P&L.
3
- *
4
- * This metric answers: "Is there a correlation between the number
5
- * of sectors a user is invested in and their average P&L?"
6
- *
7
- * REFACTOR: This calculation now aggregates the results on the server,
8
- * returning the average P&L per score, not raw data arrays.
9
- */
10
- class DiversificationPnl {
11
- constructor() {
12
- // Stores { [score]: { pnlSum: 0, count: 0 } }
13
- // The 'score' is the number of unique sectors
14
- this.pnlByScore = new Map();
15
- }
16
-
17
- /**
18
- * Defines the output schema for this calculation.
19
- * REFACTOR: Schema now describes the final aggregated result.
20
- * @returns {object} JSON Schema object
21
- */
22
- static getSchema() {
23
- const scoreSchema = {
24
- "type": "object",
25
- "description": "Aggregated P&L data for a specific diversification score.",
26
- "properties": {
27
- "average_pnl": {
28
- "type": "number",
29
- "description": "The average P&L (in USD) for all users with this score."
30
- },
31
- "user_count": {
32
- "type": "number",
33
- "description": "The number of users who had this diversification score."
34
- }
35
- },
36
- "required": ["average_pnl", "user_count"]
37
- };
38
-
39
- return {
40
- "type": "object",
41
- "description": "Calculates the average P&L correlated by diversification score (number of unique sectors).",
42
- "patternProperties": {
43
- "^[0-9]+$": scoreSchema // Key is the score (e.g., "1", "2")
44
- },
45
- "additionalProperties": scoreSchema
46
- };
47
- }
48
-
49
- _initScore(score) {
50
- if (!this.pnlByScore.has(score)) {
51
- this.pnlByScore.set(score, { pnlSum: 0, count: 0 });
52
- }
53
- }
54
-
55
- /**
56
- * @param {object} todayPortfolio
57
- * @param {object} yesterdayPortfolio
58
- */
59
- process(todayPortfolio, yesterdayPortfolio) {
60
- const tPositions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
61
- if (!tPositions || !Array.isArray(tPositions) || tPositions.length === 0) {
62
- return;
63
- }
64
-
65
- const yPortfolio = yesterdayPortfolio.Portfolio;
66
- if (!yPortfolio) return;
67
-
68
- // 1. Calculate diversification score (count of unique sectors)
69
- const sectors = new Set();
70
- for (const pos of tPositions) {
71
- if (pos.SectorID) {
72
- sectors.add(pos.SectorID);
73
- }
74
- }
75
- const diversificationScore = sectors.size;
76
-
77
- // Skip users with 0 positions/sectors
78
- if (diversificationScore === 0) {
79
- return;
80
- }
81
-
82
- // 2. Get total P&L from yesterday's snapshot
83
- const pnl = yPortfolio.Equity - yPortfolio.EquityBase; // Total P&L in USD
84
-
85
- // 3. Store P&L sum and count by score
86
- this._initScore(diversificationScore);
87
- const data = this.pnlByScore.get(diversificationScore);
88
- data.pnlSum += pnl;
89
- data.count++;
90
- }
91
-
92
- /**
93
- * REFACTOR: This method now calculates the final average P&L per score.
94
- */
95
- getResult() {
96
- const result = {};
97
-
98
- for (const [score, data] of this.pnlByScore.entries()) {
99
- if (data.count > 0) {
100
- result[score] = {
101
- average_pnl: data.pnlSum / data.count,
102
- user_count: data.count
103
- };
104
- }
105
- }
106
-
107
- return result;
108
- }
109
-
110
- reset() {
111
- this.pnlByScore.clear();
112
- }
113
- }
114
-
115
- module.exports = DiversificationPnl;
@@ -1,137 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 2) for drawdown response.
3
- *
4
- * This metric answers: "How do users behave when a position has a
5
- * drawdown of over 10%?"
6
- *
7
- * It checks all positions from yesterday that were in >10% loss
8
- * and tracks whether the user:
9
- * 1. Held (position still open today, same size)
10
- * 2. Closed (position no longer open today)
11
- * 3. Added (position still open, but Invested % is larger)
12
- */
13
- class DrawdownResponse {
14
- constructor() {
15
- this.actions = {
16
- held: 0,
17
- closed: 0,
18
- added: 0
19
- };
20
- this.total_in_drawdown = 0;
21
- }
22
-
23
- /**
24
- * Defines the output schema for this calculation.
25
- * @returns {object} JSON Schema object
26
- */
27
- static getSchema() {
28
- return {
29
- "type": "object",
30
- "description": "Analyzes user behavior in response to a >10% position drawdown.",
31
- "properties": {
32
- "total_positions_in_drawdown": {
33
- "type": "number",
34
- "description": "Total positions from yesterday that were in >10% drawdown."
35
- },
36
- "action_held_pct": {
37
- "type": "number",
38
- "description": "Percentage of drawdown positions that were held."
39
- },
40
- "action_closed_pct": {
41
- "type": "number",
42
- "description": "Percentage of drawdown positions that were closed."
43
- },
44
- "action_added_pct": {
45
- "type": "number",
46
- "description": "Percentage of drawdown positions that were added to."
47
- },
48
- "raw_counts": {
49
- "type": "object",
50
- "properties": {
51
- "held": { "type": "number" },
52
- "closed": { "type": "number" },
53
- "added": { "type": "number" }
54
- },
55
- "required": ["held", "closed", "added"]
56
- }
57
- },
58
- "required": ["total_positions_in_drawdown", "action_held_pct", "action_closed_pct", "action_added_pct", "raw_counts"]
59
- };
60
- }
61
-
62
- _getPortfolioMap(portfolio) {
63
- // We MUST use PositionID to track specific trades
64
- const positions = portfolio?.AggregatedPositions || portfolio?.PublicPositions;
65
- if (!positions || !Array.isArray(positions)) {
66
- return new Map();
67
- }
68
- // Map<PositionID, { pnl: number, invested: number }>
69
- return new Map(positions.map(p => [p.PositionID, {
70
- pnl: (p.NetProfit || 0) / (p.InvestedAmount || p.Amount || 1), // PNL as %
71
- invested: p.InvestedAmount || p.Amount || 0
72
- }]));
73
- }
74
-
75
- process(todayPortfolio, yesterdayPortfolio) {
76
- if (!todayPortfolio || !yesterdayPortfolio) {
77
- return;
78
- }
79
-
80
- const yPosMap = this._getPortfolioMap(yesterdayPortfolio);
81
- const tPosMap = this._getPortfolioMap(todayPortfolio);
82
-
83
- if (yPosMap.size === 0) {
84
- return;
85
- }
86
-
87
- for (const [yPosId, yPosData] of yPosMap.entries()) {
88
- // Check if position was in >10% drawdown
89
- if (yPosData.pnl < -0.10) {
90
- this.total_in_drawdown++;
91
-
92
- // Now, check what happened today
93
- if (!tPosMap.has(yPosId)) {
94
- // 1. Position was closed
95
- this.actions.closed++;
96
- } else {
97
- const tPosData = tPosMap.get(yPosId);
98
- // 2. Position was added to (check for > 1% increase to avoid noise)
99
- if (tPosData.invested > (yPosData.invested * 1.01)) {
100
- this.actions.added++;
101
- } else {
102
- // 3. Position was held
103
- this.actions.held++;
104
- }
105
- }
106
- }
107
- }
108
- }
109
-
110
- getResult() {
111
- const total = this.total_in_drawdown;
112
- if (total === 0) {
113
- return {
114
- total_positions_in_drawdown: 0,
115
- action_held_pct: 0,
116
- action_closed_pct: 0,
117
- action_added_pct: 0,
118
- raw_counts: this.actions
119
- };
120
- }
121
-
122
- return {
123
- total_positions_in_drawdown: total,
124
- action_held_pct: (this.actions.held / total) * 100,
125
- action_closed_pct: (this.actions.closed / total) * 100,
126
- action_added_pct: (this.actions.added / total) * 100,
127
- raw_counts: this.actions
128
- };
129
- }
130
-
131
- reset() {
132
- this.actions = { held: 0, closed: 0, added: 0 };
133
- this.total_in_drawdown = 0;
134
- }
135
- }
136
-
137
- module.exports = DrawdownResponse;