aiden-shared-calculations-unified 1.0.86 → 1.0.87

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 (70) hide show
  1. package/calculations/core/asset-pnl-status.js +36 -106
  2. package/calculations/core/asset-position-size.js +40 -91
  3. package/calculations/core/average-daily-pnl-all-users.js +18 -57
  4. package/calculations/core/average-daily-pnl-per-sector.js +41 -88
  5. package/calculations/core/average-daily-pnl-per-stock.js +38 -91
  6. package/calculations/core/average-daily-position-pnl.js +19 -49
  7. package/calculations/core/holding-duration-per-asset.js +25 -127
  8. package/calculations/core/instrument-price-change-1d.js +30 -49
  9. package/calculations/core/instrument-price-momentum-20d.js +50 -60
  10. package/calculations/core/long-position-per-stock.js +39 -68
  11. package/calculations/core/overall-holding-duration.js +16 -87
  12. package/calculations/core/overall-profitability-ratio.js +11 -40
  13. package/calculations/core/platform-buy-sell-sentiment.js +41 -124
  14. package/calculations/core/platform-daily-bought-vs-sold-count.js +41 -99
  15. package/calculations/core/platform-daily-ownership-delta.js +68 -126
  16. package/calculations/core/platform-ownership-per-sector.js +45 -96
  17. package/calculations/core/platform-total-positions-held.js +20 -80
  18. package/calculations/core/pnl-distribution-per-stock.js +29 -135
  19. package/calculations/core/price-metrics.js +95 -206
  20. package/calculations/core/profitability-ratio-per-sector.js +34 -79
  21. package/calculations/core/profitability-ratio-per-stock.js +32 -88
  22. package/calculations/core/profitability-skew-per-stock.js +41 -94
  23. package/calculations/core/profitable-and-unprofitable-status.js +44 -76
  24. package/calculations/core/sentiment-per-stock.js +24 -77
  25. package/calculations/core/short-position-per-stock.js +35 -43
  26. package/calculations/core/social-activity-aggregation.js +26 -49
  27. package/calculations/core/social-asset-posts-trend.js +38 -94
  28. package/calculations/core/social-event-correlation.js +26 -93
  29. package/calculations/core/social-sentiment-aggregation.js +20 -44
  30. package/calculations/core/social-top-mentioned-words.js +35 -87
  31. package/calculations/core/social-topic-interest-evolution.js +22 -111
  32. package/calculations/core/social-topic-sentiment-matrix.js +38 -104
  33. package/calculations/core/social-word-mentions-trend.js +27 -104
  34. package/calculations/core/speculator-asset-sentiment.js +31 -72
  35. package/calculations/core/speculator-danger-zone.js +48 -84
  36. package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +20 -52
  37. package/calculations/core/speculator-distance-to-tp-per-leverage.js +23 -53
  38. package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +20 -50
  39. package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +23 -50
  40. package/calculations/core/speculator-leverage-per-asset.js +25 -64
  41. package/calculations/core/speculator-leverage-per-sector.js +27 -63
  42. package/calculations/core/speculator-risk-reward-ratio-per-asset.js +24 -53
  43. package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +55 -68
  44. package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +54 -71
  45. package/calculations/core/speculator-stop-loss-per-asset.js +19 -44
  46. package/calculations/core/speculator-take-profit-per-asset.js +20 -57
  47. package/calculations/core/speculator-tsl-per-asset.js +17 -56
  48. package/calculations/core/total-long-figures.js +16 -31
  49. package/calculations/core/total-long-per-sector.js +39 -61
  50. package/calculations/core/total-short-figures.js +13 -32
  51. package/calculations/core/total-short-per-sector.js +39 -61
  52. package/calculations/core/users-processed.js +11 -46
  53. package/calculations/gauss/cohort-capital-flow.js +54 -173
  54. package/calculations/gauss/cohort-definer.js +77 -163
  55. package/calculations/gauss/daily-dna-filter.js +29 -83
  56. package/calculations/gauss/gauss-divergence-signal.js +22 -109
  57. package/calculations/gem/cohort-momentum-state.js +27 -72
  58. package/calculations/gem/cohort-skill-definition.js +36 -52
  59. package/calculations/gem/platform-conviction-divergence.js +18 -60
  60. package/calculations/gem/quant-skill-alpha-signal.js +25 -98
  61. package/calculations/gem/skilled-cohort-flow.js +67 -175
  62. package/calculations/gem/skilled-unskilled-divergence.js +18 -73
  63. package/calculations/gem/unskilled-cohort-flow.js +64 -172
  64. package/calculations/helix/helix-contrarian-signal.js +20 -114
  65. package/calculations/helix/herd-consensus-score.js +42 -124
  66. package/calculations/helix/winner-loser-flow.js +36 -118
  67. package/calculations/pyro/risk-appetite-index.js +33 -74
  68. package/calculations/pyro/squeeze-potential.js +30 -87
  69. package/calculations/pyro/volatility-signal.js +33 -78
  70. package/package.json +1 -1
@@ -1,170 +1,100 @@
1
1
  /**
2
2
  * @fileoverview Core Metric (Pass 2)
3
- *
4
- * This 'standard' calculation streams all user portfolios
5
- * and, for each asset, buckets users into "Winner" (in-profit)
6
- * and "Loser" (in-loss) cohorts.
7
- *
8
- * It is a dependency for calculations that need to know
9
- * the full list of users in each cohort (e.g., 'helix-contrarian-signal')
10
- * but is too large to be a dependency for 'winner-loser-flow'.
3
+ * REFACTORED: Buckets users by P&L status (Win/Loss).
11
4
  */
12
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
13
-
14
5
  class AssetPnlStatus {
15
6
  constructor() {
16
- // { [ticker]: { winners: [uid1, uid2], losers: [uid1] } }
17
7
  this.tickerBuckets = new Map();
18
- // { [sector]: { winners: [uid1, uid2], losers: [uid1] } }
19
8
  this.sectorBuckets = new Map();
20
-
21
- // --- STANDARD 0: RENAMED ---
22
- this.tickerMap = null;
23
- this.sectorMap = null;
24
-
25
9
  }
26
10
 
27
- /** Statically defines metadata */
28
11
  static getMetadata() {
29
12
  return {
30
13
  type: 'standard',
31
14
  rootDataDependencies: ['portfolio'],
32
- isHistorical: false, // Reads today's portfolio
15
+ isHistorical: false,
33
16
  userType: 'all',
34
17
  category: 'core'
35
18
  };
36
19
  }
37
20
 
38
- /** Statically declare dependencies */
39
- static getDependencies() {
40
- return []; // This is a Pass 2 calculation
41
- }
21
+ static getDependencies() { return []; }
42
22
 
43
- /**
44
- * Defines the output schema for this calculation.
45
- */
46
23
  static getSchema() {
47
24
  const cohortSchema = {
48
25
  "type": "object",
49
26
  "properties": {
50
- "winners": {
51
- "type": "array",
52
- "items": { "type": "string" },
53
- "description": "List of User IDs in the 'Winner' (in-profit) cohort."
54
- },
55
- "losers": {
56
- "type": "array",
57
- "items": { "type": "string" },
58
- "description": "List of User IDs in the 'Loser' (in-loss) cohort."
59
- }
27
+ "winners": { "type": "array", "items": { "type": "string" } },
28
+ "losers": { "type": "array", "items": { "type": "string" } }
60
29
  },
61
30
  "required": ["winners", "losers"]
62
31
  };
63
-
64
32
  return {
65
33
  "type": "object",
66
- "description": "Buckets all users into 'Winner' or 'Loser' cohorts per asset and sector.",
67
34
  "properties": {
68
- "by_ticker": {
69
- "type": "object",
70
- "patternProperties": { "^.*$": cohortSchema },
71
- "additionalProperties": cohortSchema
72
- },
73
- "by_sector": {
74
- "type": "object",
75
- "patternProperties": { "^.*$": cohortSchema },
76
- "additionalProperties": cohortSchema
77
- }
78
- },
79
- "required": ["by_ticker", "by_sector"]
35
+ "by_ticker": { "type": "object", "patternProperties": { "^.*$": cohortSchema } },
36
+ "by_sector": { "type": "object", "patternProperties": { "^.*$": cohortSchema } }
37
+ }
80
38
  };
81
39
  }
82
40
 
83
41
  _init(map, key) {
84
- if (!map.has(key)) {
85
- map.set(key, { winners: new Set(), losers: new Set() });
86
- }
42
+ if (!map.has(key)) map.set(key, { winners: new Set(), losers: new Set() });
87
43
  }
88
44
 
89
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
90
- // --- STANDARD 0: FIXED ---
91
- if (!this.tickerMap) {
92
- this.tickerMap = context.instrumentToTicker;
93
- this.sectorMap = context.sectorMapping;
94
- }
95
-
96
- if (!this.tickerMap || !this.sectorMap) {
97
- return; // Failsafe if context is missing maps
98
- }
99
-
100
- const positions = todayPortfolio?.AggregatedPositions || todayPortfolio?.PublicPositions;
101
- if (!positions || positions.length === 0) {
102
- return;
103
- }
45
+ process(context) {
46
+ const { extract } = context.math;
47
+ const { mappings, user } = context;
48
+
49
+ const positions = extract.getPositions(user.portfolio.today, user.type);
104
50
 
105
51
  for (const pos of positions) {
106
- if (!pos.InstrumentID) continue;
52
+ const instId = extract.getInstrumentId(pos);
53
+ if (!instId) continue;
107
54
 
108
- const pnl = pos.NetProfit || 0;
109
- if (pnl === 0) continue; // Ignore neutral
55
+ const pnl = extract.getNetProfit(pos);
56
+ if (pnl === 0) continue;
110
57
 
111
- // --- STANDARD 0: SIMPLIFIED ---
112
- const ticker = this.tickerMap[pos.InstrumentID];
113
- const sector = this.sectorMap[pos.InstrumentID];
58
+ const ticker = mappings.instrumentToTicker[instId];
59
+ const sector = mappings.sectorMapping[instId];
114
60
  const isWinner = pnl > 0;
115
61
 
116
62
  if (ticker) {
117
63
  this._init(this.tickerBuckets, ticker);
118
64
  const asset = this.tickerBuckets.get(ticker);
119
- if (isWinner) asset.winners.add(userId);
120
- else asset.losers.add(userId);
65
+ if (isWinner) asset.winners.add(user.id);
66
+ else asset.losers.add(user.id);
121
67
  }
122
68
 
123
69
  if (sector) {
124
70
  this._init(this.sectorBuckets, sector);
125
71
  const sec = this.sectorBuckets.get(sector);
126
- if (isWinner) sec.winners.add(userId);
127
- else sec.losers.add(userId);
72
+ if (isWinner) sec.winners.add(user.id);
73
+ else sec.losers.add(user.id);
128
74
  }
129
75
  }
130
76
  }
131
77
 
132
78
  async getResult() {
133
- // --- STANDARD 0: REMOVED forbidden data load ---
134
-
135
- // Failsafe check
136
- if (!this.tickerMap) {
137
- return { by_ticker: {}, by_sector: {} };
138
- }
139
-
140
- const result = {
141
- by_ticker: {},
142
- by_sector: {}
143
- };
144
-
145
- for (const [ticker, data] of this.tickerBuckets.entries()) {
146
- result.by_ticker[ticker] = {
147
- winners: Array.from(data.winners),
148
- losers: Array.from(data.losers)
149
- };
150
- }
151
-
152
- for (const [sector, data] of this.sectorBuckets.entries()) {
153
- result.by_sector[sector] = {
154
- winners: Array.from(data.winners),
155
- losers: Array.from(data.losers)
156
- };
157
- }
79
+ const result = { by_ticker: {}, by_sector: {} };
158
80
 
81
+ const buildResult = (map, out) => {
82
+ for (const [key, data] of map.entries()) {
83
+ out[key] = {
84
+ winners: Array.from(data.winners),
85
+ losers: Array.from(data.losers)
86
+ };
87
+ }
88
+ };
89
+
90
+ buildResult(this.tickerBuckets, result.by_ticker);
91
+ buildResult(this.sectorBuckets, result.by_sector);
159
92
  return result;
160
93
  }
161
94
 
162
95
  reset() {
163
96
  this.tickerBuckets.clear();
164
97
  this.sectorBuckets.clear();
165
- // --- STANDARD 0: RENAMED ---
166
- this.tickerMap = null;
167
- this.sectorMap = null;
168
98
  }
169
99
  }
170
100
  module.exports = AssetPnlStatus;
@@ -1,145 +1,97 @@
1
1
  /**
2
2
  * @fileoverview Core Metric (Pass 2)
3
- *
4
- * This 'standard' calculation streams all user portfolios
5
- * and calculates the *average position size in $USD* for
6
- * all holders of each asset, bucketed by ticker and sector.
3
+ * REFACTORED: Calculates average portfolio weight (%) per asset.
7
4
  */
8
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
9
-
10
5
  class AssetPositionSize {
11
6
  constructor() {
12
- // { [ticker]: { invested_sum: 0, user_count: 0 } }
13
7
  this.tickerBuckets = new Map();
14
- // { [sector]: { invested_sum: 0, user_count: 0 } }
15
8
  this.sectorBuckets = new Map();
16
-
17
- // --- STANDARD 0: RENAMED ---
18
- this.tickerMap = null;
19
- this.sectorMap = null;
20
-
21
9
  }
22
10
 
23
- /** Statically defines metadata */
24
11
  static getMetadata() {
25
12
  return {
26
13
  type: 'standard',
27
14
  rootDataDependencies: ['portfolio'],
28
- isHistorical: false, // Reads today's portfolio
15
+ isHistorical: false,
29
16
  userType: 'all',
30
17
  category: 'core'
31
18
  };
32
19
  }
33
20
 
34
- /** Statically declare dependencies */
35
- static getDependencies() {
36
- return [];
37
- }
21
+ static getDependencies() { return []; }
38
22
 
39
- /**
40
- * Defines the output schema for this calculation.
41
- */
42
23
  static getSchema() {
43
24
  const schema = {
44
25
  "type": "object",
45
26
  "properties": {
46
- "avg_position_usd": { "type": "number" },
47
- "total_invested_usd": { "type": "number" },
27
+ "avg_position_weight": { "type": "number", "description": "Average % of portfolio allocated to this asset." },
28
+ "total_exposure_weight": { "type": "number", "description": "Sum of all user allocation percentages." },
48
29
  "user_count": { "type": "number" }
49
30
  },
50
- "required": ["avg_position_usd", "total_invested_usd", "user_count"]
31
+ "required": ["avg_position_weight", "total_exposure_weight", "user_count"]
51
32
  };
52
-
53
33
  return {
54
34
  "type": "object",
55
- "description": "Calculates the avg. $USD position size for all holders per asset/sector.",
56
35
  "properties": {
57
- "by_ticker": {
58
- "type": "object",
59
- "patternProperties": { "^.*$": schema },
60
- "additionalProperties": schema
61
- },
62
- "by_sector": {
63
- "type": "object",
64
- "patternProperties": { "^.*$": schema },
65
- "additionalProperties": schema
66
- }
67
- },
68
- "required": ["by_ticker", "by_sector"]
36
+ "by_ticker": { "type": "object", "patternProperties": { "^.*$": schema } },
37
+ "by_sector": { "type": "object", "patternProperties": { "^.*$": schema } }
38
+ }
69
39
  };
70
40
  }
71
41
 
72
42
  _init(map, key) {
73
- if (!map.has(key)) {
74
- map.set(key, { invested_sum: 0, user_count: 0 });
75
- }
43
+ if (!map.has(key)) map.set(key, { weight_sum: 0, user_count: 0 });
76
44
  }
77
45
 
78
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
79
- // --- STANDARD 0: FIXED ---
80
- if (!this.tickerMap) {
81
- this.tickerMap = context.instrumentToTicker;
82
- this.sectorMap = context.sectorMapping;
83
- }
84
-
85
- if (!this.tickerMap || !this.sectorMap) {
86
- return; // Failsafe if context is missing maps
87
- }
88
-
89
- const positions = todayPortfolio?.AggregatedPositions || todayPortfolio?.PublicPositions;
90
- if (!positions || positions.length === 0) {
91
- return;
92
- }
46
+ process(context) {
47
+ const { extract } = context.math;
48
+ const { mappings, user } = context;
49
+
50
+ const positions = extract.getPositions(user.portfolio.today, user.type);
93
51
 
94
52
  for (const pos of positions) {
95
- const invested = pos.Invested || 0;
96
- if (!pos.InstrumentID || invested <= 0) continue;
53
+ const weight = extract.getPositionWeight(pos, user.type);
54
+ if (weight <= 0) continue;
55
+
56
+ const instId = extract.getInstrumentId(pos);
57
+ if (!instId) continue;
97
58
 
98
- // --- STANDARD 0: SIMPLIFIED ---
99
- const ticker = this.tickerMap[pos.InstrumentID];
100
- const sector = this.sectorMap[pos.InstrumentID];
59
+ const ticker = mappings.instrumentToTicker[instId];
60
+ const sector = mappings.sectorMapping[instId];
101
61
 
102
62
  if (ticker) {
103
63
  this._init(this.tickerBuckets, ticker);
104
64
  const asset = this.tickerBuckets.get(ticker);
105
- asset.invested_sum += invested;
106
- asset.user_count++; // Count each position
65
+ asset.weight_sum += weight;
66
+ asset.user_count++;
107
67
  }
108
68
 
109
69
  if (sector) {
110
70
  this._init(this.sectorBuckets, sector);
111
71
  const sec = this.sectorBuckets.get(sector);
112
- sec.invested_sum += invested;
113
- sec.user_count++; // Count each position
72
+ sec.weight_sum += weight;
73
+ sec.user_count++;
114
74
  }
115
75
  }
116
76
  }
117
77
 
118
78
  async getResult() {
119
- const result = {
120
- by_ticker: {},
121
- by_sector: {}
122
- };
123
-
124
- for (const [ticker, data] of this.tickerBuckets.entries()) {
125
- if (data.user_count > 0) {
126
- result.by_ticker[ticker] = {
127
- avg_position_usd: data.invested_sum / data.user_count,
128
- total_invested_usd: data.invested_sum,
129
- user_count: data.user_count
130
- };
131
- }
132
- }
79
+ const result = { by_ticker: {}, by_sector: {} };
133
80
 
134
- for (const [sector, data] of this.sectorBuckets.entries()) {
135
- if (data.user_count > 0) {
136
- result.by_sector[sector] = {
137
- avg_position_usd: data.invested_sum / data.user_count,
138
- total_invested_usd: data.invested_sum,
139
- user_count: data.user_count
140
- };
81
+ const buildResult = (map, out) => {
82
+ for (const [key, data] of map.entries()) {
83
+ if (data.user_count > 0) {
84
+ out[key] = {
85
+ avg_position_weight: data.weight_sum / data.user_count,
86
+ total_exposure_weight: data.weight_sum,
87
+ user_count: data.user_count
88
+ };
89
+ }
141
90
  }
142
- }
91
+ };
92
+
93
+ buildResult(this.tickerBuckets, result.by_ticker);
94
+ buildResult(this.sectorBuckets, result.by_sector);
143
95
 
144
96
  return result;
145
97
  }
@@ -147,9 +99,6 @@ class AssetPositionSize {
147
99
  reset() {
148
100
  this.tickerBuckets.clear();
149
101
  this.sectorBuckets.clear();
150
- // --- STANDARD 0: RENAMED ---
151
- this.tickerMap = null;
152
- this.sectorMap = null;
153
102
  }
154
103
  }
155
104
  module.exports = AssetPositionSize;
@@ -1,97 +1,58 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for average daily P&L.
3
- *
4
- * This metric answers: "What was the average daily P&L
5
- * for the average user?"
6
- *
7
- * This is a global health metric for the crowd.
2
+ * @fileoverview Calculation (Pass 1) for average daily P&L %.
3
+ * REFACTORED: Uses standardized P&L % access.
8
4
  */
9
5
  class AverageDailyPnlAllUsers {
10
6
  constructor() {
11
- this.totalPnl = 0;
7
+ this.totalPnlPct = 0;
12
8
  this.userCount = 0;
13
9
  }
14
10
 
15
- // --- NEW ---
16
- /**
17
- * Statically defines all metadata for the manifest builder.
18
- */
19
11
  static getMetadata() {
20
12
  return {
21
13
  type: 'standard',
22
- rootDataDependencies: ['portfolio'], // Needs portfolio.Summary
14
+ rootDataDependencies: ['portfolio'],
23
15
  isHistorical: false,
24
16
  userType: 'all',
25
17
  category: 'core_pnl'
26
18
  };
27
19
  }
28
20
 
29
- // --- NEW ---
30
- /**
31
- * Statically declare dependencies.
32
- */
33
- static getDependencies() {
34
- return [];
35
- }
21
+ static getDependencies() { return []; }
36
22
 
37
- /**
38
- * Defines the output schema for this calculation.
39
- * @returns {object} JSON Schema object
40
- */
41
23
  static getSchema() {
42
24
  return {
43
25
  "type": "object",
44
- "description": "Calculates the average daily P&L across all users.",
45
26
  "properties": {
46
- "average_daily_pnl": {
47
- "type": "number",
48
- "description": "The average daily P&L per user (Total P&L / User Count)."
49
- },
50
- "total_pnl": {
51
- "type": "number",
52
- "description": "The sum of all daily P&L from all users."
53
- },
54
- "user_count": {
55
- "type": "number",
56
- "description": "The total number of users processed."
57
- }
27
+ "average_daily_pnl_pct": { "type": "number" },
28
+ "total_aggregate_pnl_pct": { "type": "number", "description": "Sum of all user daily P&L percentages." },
29
+ "user_count": { "type": "number" }
58
30
  },
59
- "required": ["average_daily_pnl", "total_pnl", "user_count"]
31
+ "required": ["average_daily_pnl_pct", "total_aggregate_pnl_pct", "user_count"]
60
32
  };
61
33
  }
62
34
 
63
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
64
- let dailyPnl = 0;
65
-
66
- // Check if it's a SPECULATOR portfolio (it has a root 'NetProfit' and 'PublicPositions')
67
- if (todayPortfolio.NetProfit !== undefined && todayPortfolio.PublicPositions) {
68
-
69
- dailyPnl = todayPortfolio.NetProfit;
35
+ process(context) {
36
+ const { extract } = context.math;
37
+ const { user } = context;
70
38
 
71
- // Check if it's a NORMAL user portfolio (it has 'AggregatedPositionsByInstrumentTypeID')
72
- } else if (todayPortfolio.AggregatedPositionsByInstrumentTypeID) {
73
-
74
- // Sum the P&L by calculating (Value - Invested) from the aggregates
75
- dailyPnl = todayPortfolio.AggregatedPositionsByInstrumentTypeID.reduce((sum, agg) => {
76
- return sum + (agg.Value - agg.Invested);
77
- }, 0);
78
-
79
- }
39
+ // extract.getPortfolioDailyPnl returns the daily P&L as a percentage for the user
40
+ const dailyPnl = extract.getPortfolioDailyPnl(user.portfolio.today, user.type);
80
41
 
81
- this.totalPnl += dailyPnl;
42
+ this.totalPnlPct += dailyPnl;
82
43
  this.userCount++;
83
44
  }
84
45
 
85
46
  getResult() {
86
47
  return {
87
- average_daily_pnl: (this.userCount > 0) ? (this.totalPnl / this.userCount) : 0,
88
- total_pnl: this.totalPnl,
48
+ average_daily_pnl_pct: (this.userCount > 0) ? (this.totalPnlPct / this.userCount) : 0,
49
+ total_aggregate_pnl_pct: this.totalPnlPct,
89
50
  user_count: this.userCount
90
51
  };
91
52
  }
92
53
 
93
54
  reset() {
94
- this.totalPnl = 0;
55
+ this.totalPnlPct = 0;
95
56
  this.userCount = 0;
96
57
  }
97
58
  }