aiden-shared-calculations-unified 1.0.82 → 1.0.84

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 (71) hide show
  1. package/calculations/core/asset-pnl-status.js +122 -104
  2. package/calculations/core/asset-position-size.js +110 -73
  3. package/calculations/core/average-daily-pnl-all-users.js +17 -3
  4. package/calculations/core/average-daily-pnl-per-sector.js +83 -75
  5. package/calculations/core/average-daily-pnl-per-stock.js +84 -73
  6. package/calculations/core/average-daily-position-pnl.js +2 -2
  7. package/calculations/core/holding-duration-per-asset.js +24 -23
  8. package/calculations/core/instrument-price-change-1d.js +72 -82
  9. package/calculations/core/instrument-price-momentum-20d.js +66 -100
  10. package/calculations/core/long-position-per-stock.js +21 -13
  11. package/calculations/core/overall-holding-duration.js +8 -3
  12. package/calculations/core/overall-profitability-ratio.js +2 -2
  13. package/calculations/core/platform-buy-sell-sentiment.js +75 -22
  14. package/calculations/core/platform-daily-bought-vs-sold-count.js +19 -10
  15. package/calculations/core/platform-daily-ownership-delta.js +39 -15
  16. package/calculations/core/platform-ownership-per-sector.js +38 -18
  17. package/calculations/core/platform-total-positions-held.js +36 -14
  18. package/calculations/core/pnl-distribution-per-stock.js +39 -36
  19. package/calculations/core/price-metrics.js +70 -172
  20. package/calculations/core/profitability-ratio-per-sector.js +23 -29
  21. package/calculations/core/profitability-ratio-per-stock.js +20 -13
  22. package/calculations/core/profitability-skew-per-stock.js +20 -13
  23. package/calculations/core/profitable-and-unprofitable-status.js +34 -10
  24. package/calculations/core/sentiment-per-stock.js +20 -9
  25. package/calculations/core/short-position-per-stock.js +23 -37
  26. package/calculations/core/social-activity-aggregation.js +41 -115
  27. package/calculations/core/social-asset-posts-trend.js +77 -94
  28. package/calculations/core/social-event-correlation.js +87 -106
  29. package/calculations/core/social-sentiment-aggregation.js +56 -138
  30. package/calculations/core/social-top-mentioned-words.js +74 -106
  31. package/calculations/core/social-topic-interest-evolution.js +94 -94
  32. package/calculations/core/social-topic-sentiment-matrix.js +90 -74
  33. package/calculations/core/social-word-mentions-trend.js +92 -106
  34. package/calculations/core/speculator-asset-sentiment.js +63 -92
  35. package/calculations/core/speculator-danger-zone.js +77 -90
  36. package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +75 -90
  37. package/calculations/core/speculator-distance-to-tp-per-leverage.js +75 -88
  38. package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +75 -90
  39. package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +74 -89
  40. package/calculations/core/speculator-leverage-per-asset.js +62 -57
  41. package/calculations/core/speculator-leverage-per-sector.js +53 -65
  42. package/calculations/core/speculator-risk-reward-ratio-per-asset.js +71 -76
  43. package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +60 -81
  44. package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +57 -77
  45. package/calculations/core/speculator-stop-loss-per-asset.js +43 -80
  46. package/calculations/core/speculator-take-profit-per-asset.js +45 -69
  47. package/calculations/core/speculator-tsl-per-asset.js +42 -49
  48. package/calculations/core/total-long-figures.js +19 -19
  49. package/calculations/core/total-long-per-sector.js +39 -36
  50. package/calculations/core/total-short-figures.js +19 -19
  51. package/calculations/core/total-short-per-sector.js +39 -36
  52. package/calculations/core/users-processed.js +52 -25
  53. package/calculations/gauss/cohort-capital-flow.js +38 -29
  54. package/calculations/gauss/cohort-definer.js +17 -25
  55. package/calculations/gauss/daily-dna-filter.js +10 -4
  56. package/calculations/gauss/gauss-divergence-signal.js +28 -6
  57. package/calculations/gem/cohort-momentum-state.js +113 -92
  58. package/calculations/gem/cohort-skill-definition.js +23 -53
  59. package/calculations/gem/platform-conviction-divergence.js +62 -116
  60. package/calculations/gem/quant-skill-alpha-signal.js +107 -123
  61. package/calculations/gem/skilled-cohort-flow.js +178 -167
  62. package/calculations/gem/skilled-unskilled-divergence.js +73 -113
  63. package/calculations/gem/unskilled-cohort-flow.js +176 -166
  64. package/calculations/helix/helix-contrarian-signal.js +91 -83
  65. package/calculations/helix/herd-consensus-score.js +135 -97
  66. package/calculations/helix/winner-loser-flow.js +14 -14
  67. package/calculations/pyro/risk-appetite-index.js +121 -123
  68. package/calculations/pyro/squeeze-potential.js +93 -125
  69. package/calculations/pyro/volatility-signal.js +109 -97
  70. package/package.json +9 -9
  71. package/README.MD +0 -78
@@ -1,152 +1,170 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for asset P&L status.
2
+ * @fileoverview Core Metric (Pass 2)
3
3
  *
4
- * This metric answers: "For each asset, how many users in our
5
- * sample are in profit versus in loss?"
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.
6
7
  *
7
- * This provides a crowd-wide P&L status for each instrument.
8
- *
9
- * --- FIX ---
10
- * This version is modified to *only* store counts. The
11
- * `users_in_profit` and `users_in_loss` arrays are removed
12
- * to prevent exceeding the 1 MiB Firestore document size limit.
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'.
13
11
  */
14
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
12
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
15
13
 
16
14
  class AssetPnlStatus {
17
15
  constructor() {
18
- // We will store { [instrumentId]: { in_profit_count: 0, in_loss_count: 0 } }
19
- // We no longer store the user maps, just the counts.
20
- this.assets = new Map();
21
- this.mappings = null;
22
- this.seenUsers = new Map(); // Map<instrumentId, Set<userId>>
16
+ // { [ticker]: { winners: [uid1, uid2], losers: [uid1] } }
17
+ this.tickerBuckets = new Map();
18
+ // { [sector]: { winners: [uid1, uid2], losers: [uid1] } }
19
+ this.sectorBuckets = new Map();
20
+
21
+ // --- STANDARD 0: RENAMED ---
22
+ this.tickerMap = null;
23
+ this.sectorMap = null;
24
+
25
+ }
26
+
27
+ /** Statically defines metadata */
28
+ static getMetadata() {
29
+ return {
30
+ type: 'standard',
31
+ rootDataDependencies: ['portfolio'],
32
+ isHistorical: false, // Reads today's portfolio
33
+ userType: 'all',
34
+ category: 'core'
35
+ };
36
+ }
37
+
38
+ /** Statically declare dependencies */
39
+ static getDependencies() {
40
+ return []; // This is a Pass 2 calculation
23
41
  }
24
42
 
25
43
  /**
26
44
  * Defines the output schema for this calculation.
27
- * --- MODIFIED ---
28
- * Removed the `users_in_profit` and `users_in_loss` arrays.
29
- * @returns {object} JSON Schema object
30
45
  */
31
46
  static getSchema() {
32
- const tickerSchema = {
47
+ const cohortSchema = {
33
48
  "type": "object",
34
- "description": "P&L status for a specific asset.",
35
49
  "properties": {
36
- "in_profit_count": {
37
- "type": "number",
38
- "description": "Count of unique users currently in profit on this asset."
50
+ "winners": {
51
+ "type": "array",
52
+ "items": { "type": "string" },
53
+ "description": "List of User IDs in the 'Winner' (in-profit) cohort."
39
54
  },
40
- "in_loss_count": {
41
- "type": "number",
42
- "description": "Count of unique users currently in loss on this asset."
43
- },
44
- "profit_ratio": {
45
- "type": "number",
46
- "description": "Percentage of users in profit (In Profit / Total)."
55
+ "losers": {
56
+ "type": "array",
57
+ "items": { "type": "string" },
58
+ "description": "List of User IDs in the 'Loser' (in-loss) cohort."
47
59
  }
48
60
  },
49
- "required": ["in_profit_count", "in_loss_count", "profit_ratio"]
61
+ "required": ["winners", "losers"]
50
62
  };
51
-
63
+
52
64
  return {
53
65
  "type": "object",
54
- "description": "Tracks the number of users in profit vs. in loss for each asset.",
55
- "patternProperties": {
56
- "^.*$": tickerSchema // Ticker
66
+ "description": "Buckets all users into 'Winner' or 'Loser' cohorts per asset and sector.",
67
+ "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
+ }
57
78
  },
58
- "additionalProperties": tickerSchema
59
- };
60
- }
61
-
62
- /**
63
- * Statically defines all metadata for the manifest builder.
64
- */
65
- static getMetadata() {
66
- return {
67
- type: 'standard',
68
- rootDataDependencies: ['portfolio'],
69
- isHistorical: false,
70
- userType: 'all',
71
- category: 'core_pnl'
79
+ "required": ["by_ticker", "by_sector"]
72
80
  };
73
81
  }
74
-
75
- /**
76
- * Statically declare dependencies.
77
- */
78
- static getDependencies() {
79
- return [];
80
- }
81
-
82
- _initAsset(instrumentId) {
83
- if (!this.assets.has(instrumentId)) {
84
- this.assets.set(instrumentId, {
85
- in_profit: new Set(),
86
- in_loss: new Set()
87
- });
82
+
83
+ _init(map, key) {
84
+ if (!map.has(key)) {
85
+ map.set(key, { winners: new Set(), losers: new Set() });
88
86
  }
89
87
  }
90
88
 
91
- process(portfolioData, yesterdayPortfolio, userId) {
92
- const positions = portfolioData.PublicPositions || portfolioData.AggregatedPositions;
93
- if (!positions || !Array.isArray(positions)) {
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) {
94
102
  return;
95
103
  }
96
104
 
97
105
  for (const pos of positions) {
98
- const instrumentId = pos.InstrumentID;
99
- if (!instrumentId) continue;
106
+ if (!pos.InstrumentID) continue;
100
107
 
101
- this._initAsset(instrumentId);
102
- const asset = this.assets.get(instrumentId);
103
108
  const pnl = pos.NetProfit || 0;
109
+ if (pnl === 0) continue; // Ignore neutral
110
+
111
+ // --- STANDARD 0: SIMPLIFIED ---
112
+ const ticker = this.tickerMap[pos.InstrumentID];
113
+ const sector = this.sectorMap[pos.InstrumentID];
114
+ const isWinner = pnl > 0;
104
115
 
105
- // Only count one user once per asset
106
- if (pnl > 0) {
107
- asset.in_profit.add(userId);
108
- asset.in_loss.delete(userId); // Ensure user isn't in both
109
- } else if (pnl < 0) {
110
- asset.in_loss.add(userId);
111
- asset.in_profit.delete(userId); // Ensure user isn't in both
116
+ if (ticker) {
117
+ this._init(this.tickerBuckets, ticker);
118
+ const asset = this.tickerBuckets.get(ticker);
119
+ if (isWinner) asset.winners.add(userId);
120
+ else asset.losers.add(userId);
121
+ }
122
+
123
+ if (sector) {
124
+ this._init(this.sectorBuckets, sector);
125
+ const sec = this.sectorBuckets.get(sector);
126
+ if (isWinner) sec.winners.add(userId);
127
+ else sec.losers.add(userId);
112
128
  }
113
129
  }
114
130
  }
115
131
 
116
- /**
117
- * --- MODIFIED ---
118
- * This now saves only counts.
119
- */
120
132
  async getResult() {
121
- if (!this.mappings) {
122
- this.mappings = await loadInstrumentMappings();
133
+ // --- STANDARD 0: REMOVED forbidden data load ---
134
+
135
+ // Failsafe check
136
+ if (!this.tickerMap) {
137
+ return { by_ticker: {}, by_sector: {} };
123
138
  }
124
139
 
125
- const result = {};
126
- for (const [instrumentId, data] of this.assets.entries()) {
127
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
128
-
129
- const profitCount = data.in_profit.size;
130
- const lossCount = data.in_loss.size;
131
- const total = profitCount + lossCount;
132
-
133
- if (total > 0) {
134
- result[ticker] = {
135
- in_profit_count: profitCount,
136
- in_loss_count: lossCount,
137
- profit_ratio: (profitCount / total) * 100
138
- // Removed the user arrays
139
- };
140
- }
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
+ };
141
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
+ }
158
+
142
159
  return result;
143
160
  }
144
-
161
+
145
162
  reset() {
146
- this.assets.clear();
147
- this.mappings = null;
148
- this.seenUsers.clear();
163
+ this.tickerBuckets.clear();
164
+ this.sectorBuckets.clear();
165
+ // --- STANDARD 0: RENAMED ---
166
+ this.tickerMap = null;
167
+ this.sectorMap = null;
149
168
  }
150
169
  }
151
-
152
170
  module.exports = AssetPnlStatus;
@@ -1,118 +1,155 @@
1
1
  /**
2
- * Calculates the average position size (as a portfolio percentage) for each asset.
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
7
  */
4
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
8
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
5
9
 
6
10
  class AssetPositionSize {
7
11
  constructor() {
8
- this.assets = {};
9
- this.mappings = null;
12
+ // { [ticker]: { invested_sum: 0, user_count: 0 } }
13
+ this.tickerBuckets = new Map();
14
+ // { [sector]: { invested_sum: 0, user_count: 0 } }
15
+ this.sectorBuckets = new Map();
16
+
17
+ // --- STANDARD 0: RENAMED ---
18
+ this.tickerMap = null;
19
+ this.sectorMap = null;
20
+
10
21
  }
11
22
 
12
- // --- NEW ---
13
- /**
14
- * Statically defines all metadata for the manifest builder.
15
- */
23
+ /** Statically defines metadata */
16
24
  static getMetadata() {
17
25
  return {
18
- type: 'standard', // It processes each user
19
- rootDataDependencies: ['portfolio'], // It only needs portfolio data
20
- isHistorical: false, // It only needs today's portfolio
21
- userType: 'all', // It runs for all users
22
- category: 'core_metrics' // Belongs to the core product line
26
+ type: 'standard',
27
+ rootDataDependencies: ['portfolio'],
28
+ isHistorical: false, // Reads today's portfolio
29
+ userType: 'all',
30
+ category: 'core'
23
31
  };
24
32
  }
25
33
 
26
- // --- NEW ---
27
- /**
28
- * Statically declare dependencies.
29
- */
34
+ /** Statically declare dependencies */
30
35
  static getDependencies() {
31
- return []; // This is a Pass 1 calculation
36
+ return [];
32
37
  }
33
38
 
34
39
  /**
35
40
  * Defines the output schema for this calculation.
36
- * @returns {object} JSON Schema object
37
41
  */
38
42
  static getSchema() {
43
+ const schema = {
44
+ "type": "object",
45
+ "properties": {
46
+ "avg_position_usd": { "type": "number" },
47
+ "total_invested_usd": { "type": "number" },
48
+ "user_count": { "type": "number" }
49
+ },
50
+ "required": ["avg_position_usd", "total_invested_usd", "user_count"]
51
+ };
52
+
39
53
  return {
40
54
  "type": "object",
41
- "description": "Calculates the average position size (as a portfolio percentage) and holder count for each asset.",
42
- "patternProperties": {
43
- // This matches any string key (which will be a ticker)
44
- "^.*$": {
55
+ "description": "Calculates the avg. $USD position size for all holders per asset/sector.",
56
+ "properties": {
57
+ "by_ticker": {
58
+ "type": "object",
59
+ "patternProperties": { "^.*$": schema },
60
+ "additionalProperties": schema
61
+ },
62
+ "by_sector": {
45
63
  "type": "object",
46
- "description": "Metrics for a specific asset ticker.",
47
- "properties": {
48
- "average_position_size": {
49
- "type": "number",
50
- "description": "The average portfolio percentage allocated to this asset by users who hold it."
51
- },
52
- "position_count": {
53
- "type": "number",
54
- "description": "The total number of positions held in this asset across the user sample."
55
- }
56
- },
57
- "required": ["average_position_size", "position_count"]
64
+ "patternProperties": { "^.*$": schema },
65
+ "additionalProperties": schema
58
66
  }
59
67
  },
60
- "additionalProperties": {
61
- "type": "object",
62
- "properties": {
63
- "average_position_size": { "type": "number" },
64
- "position_count": { "type": "number" }
65
- }
66
- }
68
+ "required": ["by_ticker", "by_sector"]
67
69
  };
68
70
  }
71
+
72
+ _init(map, key) {
73
+ if (!map.has(key)) {
74
+ map.set(key, { invested_sum: 0, user_count: 0 });
75
+ }
76
+ }
69
77
 
70
- // --- REFACTORED ---
71
- // Simplified signature. This calc doesn't need history, context, or insights.
72
- process(portfolioData) {
73
- const positions = portfolioData.AggregatedPositions || portfolioData.PublicPositions;
74
- if (!positions || !Array.isArray(positions)) return;
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
+ }
75
93
 
76
- for (const position of positions) {
77
- const instrumentId = position.InstrumentID;
78
- if (!instrumentId) continue;
94
+ for (const pos of positions) {
95
+ const invested = pos.Invested || 0;
96
+ if (!pos.InstrumentID || invested <= 0) continue;
97
+
98
+ // --- STANDARD 0: SIMPLIFIED ---
99
+ const ticker = this.tickerMap[pos.InstrumentID];
100
+ const sector = this.sectorMap[pos.InstrumentID];
79
101
 
80
- if (!this.assets[instrumentId]) {
81
- this.assets[instrumentId] = { position_count: 0, position_value_sum: 0 };
102
+ if (ticker) {
103
+ this._init(this.tickerBuckets, ticker);
104
+ const asset = this.tickerBuckets.get(ticker);
105
+ asset.invested_sum += invested;
106
+ asset.user_count++; // Count each position
107
+ }
108
+
109
+ if (sector) {
110
+ this._init(this.sectorBuckets, sector);
111
+ const sec = this.sectorBuckets.get(sector);
112
+ sec.invested_sum += invested;
113
+ sec.user_count++; // Count each position
82
114
  }
83
-
84
- this.assets[instrumentId].position_count++;
85
- // FIX: Use the 'Invested' field, which holds the portfolio percentage
86
- this.assets[instrumentId].position_value_sum += (position.Invested || 0);
87
115
  }
88
116
  }
89
117
 
90
118
  async getResult() {
91
- if (!this.mappings) {
92
- this.mappings = await loadInstrumentMappings();
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
+ }
93
132
  }
94
-
95
- const result = {};
96
- for (const instrumentId in this.assets) {
97
- const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
98
- const data = this.assets[instrumentId];
99
-
100
- if (data.position_count > 0) {
101
- result[ticker] = {
102
- // This is now the average *percentage* size
103
- average_position_size: data.position_value_sum / data.position_count,
104
- position_count: data.position_count
133
+
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
105
140
  };
106
141
  }
107
142
  }
108
143
 
109
144
  return result;
110
145
  }
111
-
146
+
112
147
  reset() {
113
- this.assets = {};
114
- this.mappings = null;
148
+ this.tickerBuckets.clear();
149
+ this.sectorBuckets.clear();
150
+ // --- STANDARD 0: RENAMED ---
151
+ this.tickerMap = null;
152
+ this.sectorMap = null;
115
153
  }
116
154
  }
117
-
118
155
  module.exports = AssetPositionSize;
@@ -60,9 +60,23 @@ class AverageDailyPnlAllUsers {
60
60
  };
61
61
  }
62
62
 
63
- process(portfolioData) {
64
- // Use the P&L from the summary, which is for the *day*
65
- const dailyPnl = portfolioData.Summary?.NetProfit || 0;
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;
70
+
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
+ }
66
80
 
67
81
  this.totalPnl += dailyPnl;
68
82
  this.userCount++;