aiden-shared-calculations-unified 1.0.83 → 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 -16
  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 +5 -5
  71. package/README.MD +0 -155
@@ -1,20 +1,19 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
2
+ * @fileoverview Calculation (Pass 1) for speculator TSL usage.
3
3
  *
4
- * This metric answers: "For each asset, how many
5
- * speculators have Trailing Stop Loss (TSL) enabled
6
- * versus disabled?"
4
+ * This metric answers: "For each asset, what percentage
5
+ * of speculators are using a *trailing* stop-loss?"
7
6
  */
8
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
7
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
9
8
 
10
- class TslPerAsset {
9
+ class SpeculatorTSLPerAsset {
11
10
  constructor() {
12
- // { [instrumentId]: { enabled: 0, disabled: 0 } }
11
+ // { [instrumentId]: { with_tsl: 0, total: 0 } }
13
12
  this.assets = new Map();
14
- this.mappings = null;
13
+ // --- STANDARD 0: RENAMED ---
14
+ this.tickerMap = null;
15
15
  }
16
16
 
17
- // --- NEW ---
18
17
  /**
19
18
  * Statically defines all metadata for the manifest builder.
20
19
  */
@@ -23,12 +22,11 @@ class TslPerAsset {
23
22
  type: 'standard',
24
23
  rootDataDependencies: ['portfolio'],
25
24
  isHistorical: false,
26
- userType: 'speculator',
25
+ userType: 'speculator', // <-- KEY: Only runs for speculators
27
26
  category: 'core_speculator'
28
27
  };
29
28
  }
30
29
 
31
- // --- NEW ---
32
30
  /**
33
31
  * Statically declare dependencies.
34
32
  */
@@ -38,49 +36,40 @@ class TslPerAsset {
38
36
 
39
37
  /**
40
38
  * Defines the output schema for this calculation.
41
- * @returns {object} JSON Schema object
42
39
  */
43
40
  static getSchema() {
44
41
  const tickerSchema = {
45
42
  "type": "object",
46
43
  "properties": {
47
- "tsl_enabled_count": { "type": "number" },
48
- "tsl_disabled_count": { "type": "number" },
49
- "tsl_enabled_pct": {
50
- "type": "number",
51
- "description": "Percentage of positions with TSL enabled."
52
- }
44
+ "tsl_usage_pct": { "type": "number" },
45
+ "tsl_count": { "type": "number" },
46
+ "total_count": { "type": "number" }
53
47
  },
54
- "required": ["tsl_enabled_count", "tsl_disabled_count", "tsl_enabled_pct"]
48
+ "required": ["tsl_usage_pct", "tsl_count", "total_count"]
55
49
  };
56
50
 
57
51
  return {
58
52
  "type": "object",
59
- "description": "Calculates the TSL (Trailing Stop Loss) enabled vs. disabled rate per asset.",
60
- "patternProperties": {
61
- "^.*$": tickerSchema // Ticker
62
- },
53
+ "description": "Calculates the percentage of speculators using a *trailing* stop-loss per asset.",
54
+ "patternProperties": { "^.*$": tickerSchema },
63
55
  "additionalProperties": tickerSchema
64
56
  };
65
57
  }
66
58
 
67
59
  _initAsset(instrumentId) {
68
60
  if (!this.assets.has(instrumentId)) {
69
- this.assets.set(instrumentId, {
70
- enabled: 0,
71
- disabled: 0
72
- });
61
+ this.assets.set(instrumentId, { with_tsl: 0, total: 0 });
73
62
  }
74
63
  }
75
64
 
76
- // --- REFACTORED ---
77
- // Simplified signature
78
- process(portfolioData) {
79
- if (portfolioData?.context?.userType !== 'speculator') {
80
- return;
65
+ // --- STANDARD 0: UPDATED SIGNATURE ---
66
+ process(todayPortfolio, yesterdayPortfolio, userId, context) {
67
+ // --- STANDARD 0: ADDED ---
68
+ if (!this.tickerMap) {
69
+ this.tickerMap = context.instrumentToTicker;
81
70
  }
82
-
83
- const positions = portfolioData.PublicPositions;
71
+
72
+ const positions = todayPortfolio.PublicPositions;
84
73
  if (!positions || !Array.isArray(positions)) {
85
74
  return;
86
75
  }
@@ -91,30 +80,33 @@ class TslPerAsset {
91
80
 
92
81
  this._initAsset(instrumentId);
93
82
  const assetData = this.assets.get(instrumentId);
94
-
95
- if (pos.IsTslEnabled) {
96
- assetData.enabled++;
97
- } else {
98
- assetData.disabled++;
83
+ assetData.total++;
84
+
85
+ // HasTrailingStopLoss is a boolean
86
+ if (pos.HasTrailingStopLoss === true) {
87
+ assetData.with_tsl++;
99
88
  }
100
89
  }
101
90
  }
102
91
 
103
92
  async getResult() {
104
- if (!this.mappings) {
105
- this.mappings = await loadInstrumentMappings();
93
+ // --- STANDARD 0: REMOVED forbidden data load ---
94
+
95
+ // Failsafe check
96
+ if (!this.tickerMap) {
97
+ return {}; // process() must run first
106
98
  }
107
99
 
108
100
  const result = {};
109
101
  for (const [instrumentId, data] of this.assets.entries()) {
110
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
111
- const total = data.enabled + data.disabled;
102
+ // --- STANDARD 0: SIMPLIFIED ---
103
+ const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
112
104
 
113
- if (total > 0) {
105
+ if (data.total > 0) {
114
106
  result[ticker] = {
115
- tsl_enabled_count: data.enabled,
116
- tsl_disabled_count: data.disabled,
117
- tsl_enabled_pct: (data.enabled / total) * 100
107
+ tsl_usage_pct: (data.with_tsl / data.total) * 100,
108
+ tsl_count: data.with_tsl,
109
+ total_count: data.total
118
110
  };
119
111
  }
120
112
  }
@@ -123,8 +115,9 @@ class TslPerAsset {
123
115
 
124
116
  reset() {
125
117
  this.assets.clear();
126
- this.mappings = null;
118
+ // --- STANDARD 0: RENAMED ---
119
+ this.tickerMap = null;
127
120
  }
128
121
  }
129
122
 
130
- module.exports = TslPerAsset;
123
+ module.exports = SpeculatorTSLPerAsset;
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for total long figures.
3
3
  *
4
- * This metric answers: "What is the total number of long
5
- * ('buy') positions across all users and instruments?"
4
+ * This metric answers: "What is the total $USD value
5
+ * and total count of all long ('buy') positions?"
6
6
  */
7
7
  class TotalLongFigures {
8
8
  constructor() {
9
- this.longCount = 0;
9
+ this.totalInvestedUsd = 0;
10
+ this.totalPositions = 0;
10
11
  }
11
12
 
12
- // --- NEW ---
13
13
  /**
14
14
  * Statically defines all metadata for the manifest builder.
15
15
  */
@@ -23,7 +23,6 @@ class TotalLongFigures {
23
23
  };
24
24
  }
25
25
 
26
- // --- NEW ---
27
26
  /**
28
27
  * Statically declare dependencies.
29
28
  */
@@ -33,45 +32,46 @@ class TotalLongFigures {
33
32
 
34
33
  /**
35
34
  * Defines the output schema for this calculation.
36
- * @returns {object} JSON Schema object
37
35
  */
38
36
  static getSchema() {
39
37
  return {
40
38
  "type": "object",
41
- "description": "Calculates the total count of all long ('buy') positions.",
39
+ "description": "Aggregates the total $USD value and count of all long positions.",
42
40
  "properties": {
43
- "total_long_positions": {
44
- "type": "number",
45
- "description": "The aggregated count of all long positions."
46
- }
41
+ "total_invested_usd": { "type": "number" },
42
+ "total_positions_count": { "type": "number" }
47
43
  },
48
- "required": ["total_long_positions"]
44
+ "required": ["total_invested_usd", "total_positions_count"]
49
45
  };
50
46
  }
51
47
 
52
- // --- REFACTORED ---
53
- // Simplified signature
54
- process(portfolioData) {
55
- const positions = portfolioData.PublicPositions || portfolioData.AggregatedPositions;
48
+ // --- UPDATED SIGNATURE ---
49
+ process(todayPortfolio, yesterdayPortfolio, userId, context) {
50
+ // --- UPDATED ---
51
+ const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
56
52
  if (!positions || !Array.isArray(positions)) {
57
53
  return;
58
54
  }
59
55
 
60
56
  for (const pos of positions) {
57
+ // Only count 'buy' (long) positions
61
58
  if (pos.IsBuy) {
62
- this.longCount++;
59
+ this.totalInvestedUsd += pos.Invested || 0;
60
+ this.totalPositions++;
63
61
  }
64
62
  }
65
63
  }
66
64
 
67
65
  getResult() {
68
66
  return {
69
- total_long_positions: this.longCount
67
+ total_invested_usd: this.totalInvestedUsd,
68
+ total_positions_count: this.totalPositions
70
69
  };
71
70
  }
72
71
 
73
72
  reset() {
74
- this.longCount = 0;
73
+ this.totalInvestedUsd = 0;
74
+ this.totalPositions = 0;
75
75
  }
76
76
  }
77
77
 
@@ -1,19 +1,19 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for total long positions per sector.
2
+ * @fileoverview Calculation (Pass 1) for total long value per sector.
3
3
  *
4
- * This metric answers: "What is the total number of long ('buy')
5
- * positions for each sector?"
4
+ * This metric answers: "What is the total $USD value
5
+ * of all long ('buy') positions, grouped by sector?"
6
6
  */
7
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
7
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
8
 
9
9
  class TotalLongPerSector {
10
10
  constructor() {
11
- // We will store { [sector]: count }
11
+ // { [sectorName]: { total_invested_usd: 0, total_positions_count: 0 } }
12
12
  this.sectors = new Map();
13
- this.mappings = null;
13
+ // --- STANDARD 0: RENAMED ---
14
+ this.sectorMap = null;
14
15
  }
15
16
 
16
- // --- NEW ---
17
17
  /**
18
18
  * Statically defines all metadata for the manifest builder.
19
19
  */
@@ -27,7 +27,6 @@ class TotalLongPerSector {
27
27
  };
28
28
  }
29
29
 
30
- // --- NEW ---
31
30
  /**
32
31
  * Statically declare dependencies.
33
32
  */
@@ -37,39 +36,39 @@ class TotalLongPerSector {
37
36
 
38
37
  /**
39
38
  * Defines the output schema for this calculation.
40
- * @returns {object} JSON Schema object
41
39
  */
42
40
  static getSchema() {
43
- return {
41
+ const sectorSchema = {
44
42
  "type": "object",
45
- "description": "Calculates the total count of long ('buy') positions per sector.",
46
- "patternProperties": {
47
- // Sector name
48
- "^.*$": {
49
- "type": "number",
50
- "description": "The total count of long positions for this sector."
51
- }
52
- },
53
- "additionalProperties": {
54
- "type": "number"
43
+ "properties": {
44
+ "total_invested_usd": { "type": "number" },
45
+ "total_positions_count": { "type": "number" }
55
46
  }
56
47
  };
48
+
49
+ return {
50
+ "type": "object",
51
+ "description": "Calculates the total $USD value and count of long positions per sector.",
52
+ "patternProperties": { "^.*$": sectorSchema },
53
+ "additionalProperties": sectorSchema
54
+ };
57
55
  }
58
56
 
59
- _initSector(sector) {
60
- if (!this.sectors.has(sector)) {
61
- this.sectors.set(sector, 0);
57
+ _initSector(sectorName) {
58
+ if (!this.sectors.has(sectorName)) {
59
+ this.sectors.set(sectorName, { total_invested_usd: 0, total_positions_count: 0 });
62
60
  }
63
61
  }
64
62
 
65
- process(portfolioData, yesterdayPortfolio, userId, context) {
66
- // This calculation needs the sector mappings from Pass 1 context
67
- if (!this.mappings) {
68
- this.mappings = context.mappings;
63
+ // --- STANDARD 0: UPDATED SIGNATURE ---
64
+ async process(todayPortfolio, yesterdayPortfolio, userId, context) {
65
+ // --- STANDARD 0: FIXED ---
66
+ if (!this.sectorMap) {
67
+ this.sectorMap = context.sectorMapping;
69
68
  }
70
69
 
71
- const positions = portfolioData.PublicPositions || portfolioData.AggregatedPositions;
72
- if (!positions || !Array.isArray(positions) || !this.mappings) {
70
+ const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
71
+ if (!positions || !Array.isArray(positions) || !this.sectorMap) {
73
72
  return;
74
73
  }
75
74
 
@@ -79,22 +78,26 @@ class TotalLongPerSector {
79
78
  const instrumentId = pos.InstrumentID;
80
79
  if (!instrumentId) continue;
81
80
 
82
- const sector = this.mappings.instrumentToSector[instrumentId] || 'Other';
83
- this._initSector(sector);
84
-
85
- this.sectors.set(sector, this.sectors.get(sector) + 1);
81
+ // --- STANDARD 0: FIXED ---
82
+ const sectorName = this.sectorMap[instrumentId] || 'N/A';
83
+ this._initSector(sectorName);
84
+ const sectorData = this.sectors.get(sectorName);
85
+
86
+ sectorData.total_invested_usd += pos.Invested || 0;
87
+ sectorData.total_positions_count++;
86
88
  }
87
89
  }
88
90
  }
89
91
 
90
- getResult() {
91
- // Convert Map to plain object
92
+ async getResult() {
93
+ // --- STANDARD 0: REMOVED forbidden data load ---
92
94
  return Object.fromEntries(this.sectors);
93
95
  }
94
96
 
95
97
  reset() {
96
98
  this.sectors.clear();
97
- this.mappings = null;
99
+ // --- STANDARD 0: RENAMED ---
100
+ this.sectorMap = null;
98
101
  }
99
102
  }
100
103
 
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for total short figures.
3
3
  *
4
- * This metric answers: "What is the total number of short
5
- * ('sell') positions across all users and instruments?"
4
+ * This metric answers: "What is the total $USD value
5
+ * and total count of all short ('sell') positions?"
6
6
  */
7
7
  class TotalShortFigures {
8
8
  constructor() {
9
- this.shortCount = 0;
9
+ this.totalInvestedUsd = 0;
10
+ this.totalPositions = 0;
10
11
  }
11
12
 
12
- // --- NEW ---
13
13
  /**
14
14
  * Statically defines all metadata for the manifest builder.
15
15
  */
@@ -23,7 +23,6 @@ class TotalShortFigures {
23
23
  };
24
24
  }
25
25
 
26
- // --- NEW ---
27
26
  /**
28
27
  * Statically declare dependencies.
29
28
  */
@@ -33,45 +32,46 @@ class TotalShortFigures {
33
32
 
34
33
  /**
35
34
  * Defines the output schema for this calculation.
36
- * @returns {object} JSON Schema object
37
35
  */
38
36
  static getSchema() {
39
37
  return {
40
38
  "type": "object",
41
- "description": "Calculates the total count of all short ('sell') positions.",
39
+ "description": "Aggregates the total $USD value and count of all short positions.",
42
40
  "properties": {
43
- "total_short_positions": {
44
- "type": "number",
45
- "description": "The aggregated count of all short positions."
46
- }
41
+ "total_invested_usd": { "type": "number" },
42
+ "total_positions_count": { "type": "number" }
47
43
  },
48
- "required": ["total_short_positions"]
44
+ "required": ["total_invested_usd", "total_positions_count"]
49
45
  };
50
46
  }
51
47
 
52
- // --- REFACTORED ---
53
- // Simplified signature
54
- process(portfolioData) {
55
- const positions = portfolioData.PublicPositions || portfolioData.AggregatedPositions;
48
+ // --- UPDATED SIGNATURE ---
49
+ process(todayPortfolio, yesterdayPortfolio, userId, context) {
50
+ // --- UPDATED ---
51
+ const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
56
52
  if (!positions || !Array.isArray(positions)) {
57
53
  return;
58
54
  }
59
55
 
60
56
  for (const pos of positions) {
57
+ // Only count 'sell' (short) positions
61
58
  if (!pos.IsBuy) {
62
- this.shortCount++;
59
+ this.totalInvestedUsd += pos.Invested || 0;
60
+ this.totalPositions++;
63
61
  }
64
62
  }
65
63
  }
66
64
 
67
65
  getResult() {
68
66
  return {
69
- total_short_positions: this.shortCount
67
+ total_invested_usd: this.totalInvestedUsd,
68
+ total_positions_count: this.totalPositions
70
69
  };
71
70
  }
72
71
 
73
72
  reset() {
74
- this.shortCount = 0;
73
+ this.totalInvestedUsd = 0;
74
+ this.totalPositions = 0;
75
75
  }
76
76
  }
77
77
 
@@ -1,19 +1,19 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for total short positions per sector.
2
+ * @fileoverview Calculation (Pass 1) for total short value per sector.
3
3
  *
4
- * This metric answers: "What is the total number of short ('sell')
5
- * positions for each sector?"
4
+ * This metric answers: "What is the total $USD value
5
+ * of all short ('sell') positions, grouped by sector?"
6
6
  */
7
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
7
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
8
 
9
9
  class TotalShortPerSector {
10
10
  constructor() {
11
- // We will store { [sector]: count }
11
+ // { [sectorName]: { total_invested_usd: 0, total_positions_count: 0 } }
12
12
  this.sectors = new Map();
13
- this.mappings = null;
13
+ // --- STANDARD 0: RENAMED ---
14
+ this.sectorMap = null;
14
15
  }
15
16
 
16
- // --- NEW ---
17
17
  /**
18
18
  * Statically defines all metadata for the manifest builder.
19
19
  */
@@ -27,7 +27,6 @@ class TotalShortPerSector {
27
27
  };
28
28
  }
29
29
 
30
- // --- NEW ---
31
30
  /**
32
31
  * Statically declare dependencies.
33
32
  */
@@ -37,39 +36,39 @@ class TotalShortPerSector {
37
36
 
38
37
  /**
39
38
  * Defines the output schema for this calculation.
40
- * @returns {object} JSON Schema object
41
39
  */
42
40
  static getSchema() {
43
- return {
41
+ const sectorSchema = {
44
42
  "type": "object",
45
- "description": "Calculates the total count of short ('sell') positions per sector.",
46
- "patternProperties": {
47
- // Sector name
48
- "^.*$": {
49
- "type": "number",
50
- "description": "The total count of short positions for this sector."
51
- }
52
- },
53
- "additionalProperties": {
54
- "type": "number"
43
+ "properties": {
44
+ "total_invested_usd": { "type": "number" },
45
+ "total_positions_count": { "type": "number" }
55
46
  }
56
47
  };
48
+
49
+ return {
50
+ "type": "object",
51
+ "description": "Calculates the total $USD value and count of short positions per sector.",
52
+ "patternProperties": { "^.*$": sectorSchema },
53
+ "additionalProperties": sectorSchema
54
+ };
57
55
  }
58
56
 
59
- _initSector(sector) {
60
- if (!this.sectors.has(sector)) {
61
- this.sectors.set(sector, 0);
57
+ _initSector(sectorName) {
58
+ if (!this.sectors.has(sectorName)) {
59
+ this.sectors.set(sectorName, { total_invested_usd: 0, total_positions_count: 0 });
62
60
  }
63
61
  }
64
62
 
65
- process(portfolioData, yesterdayPortfolio, userId, context) {
66
- // This calculation needs the sector mappings from Pass 1 context
67
- if (!this.mappings) {
68
- this.mappings = context.mappings;
63
+ // --- STANDARD 0: UPDATED SIGNATURE ---
64
+ async process(todayPortfolio, yesterdayPortfolio, userId, context) {
65
+ // --- STANDARD 0: FIXED ---
66
+ if (!this.sectorMap) {
67
+ this.sectorMap = context.sectorMapping;
69
68
  }
70
69
 
71
- const positions = portfolioData.PublicPositions || portfolioData.AggregatedPositions;
72
- if (!positions || !Array.isArray(positions) || !this.mappings) {
70
+ const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
71
+ if (!positions || !Array.isArray(positions) || !this.sectorMap) {
73
72
  return;
74
73
  }
75
74
 
@@ -79,22 +78,26 @@ class TotalShortPerSector {
79
78
  const instrumentId = pos.InstrumentID;
80
79
  if (!instrumentId) continue;
81
80
 
82
- const sector = this.mappings.instrumentToSector[instrumentId] || 'Other';
83
- this._initSector(sector);
84
-
85
- this.sectors.set(sector, this.sectors.get(sector) + 1);
81
+ // --- STANDARD 0: FIXED ---
82
+ const sectorName = this.sectorMap[instrumentId] || 'N/A';
83
+ this._initSector(sectorName);
84
+ const sectorData = this.sectors.get(sectorName);
85
+
86
+ sectorData.total_invested_usd += pos.Invested || 0;
87
+ sectorData.total_positions_count++;
86
88
  }
87
89
  }
88
90
  }
89
91
 
90
- getResult() {
91
- // Convert Map to plain object
92
+ async getResult() {
93
+ // --- STANDARD 0: REMOVED forbidden data load ---
92
94
  return Object.fromEntries(this.sectors);
93
95
  }
94
96
 
95
97
  reset() {
96
98
  this.sectors.clear();
97
- this.mappings = null;
99
+ // --- STANDARD 0: RENAMED ---
100
+ this.sectorMap = null;
98
101
  }
99
102
  }
100
103