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,42 +1,25 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for speculator TSL usage.
3
- *
4
- * This metric answers: "For each asset, what percentage
5
- * of speculators are using a *trailing* stop-loss?"
3
+ * REFACTORED: Uses extraction for TSL boolean.
6
4
  */
7
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
-
9
5
  class SpeculatorTSLPerAsset {
10
6
  constructor() {
11
- // { [instrumentId]: { with_tsl: 0, total: 0 } }
12
7
  this.assets = new Map();
13
- // --- STANDARD 0: RENAMED ---
14
8
  this.tickerMap = null;
15
9
  }
16
10
 
17
- /**
18
- * Statically defines all metadata for the manifest builder.
19
- */
20
11
  static getMetadata() {
21
12
  return {
22
13
  type: 'standard',
23
14
  rootDataDependencies: ['portfolio'],
24
15
  isHistorical: false,
25
- userType: 'speculator', // <-- KEY: Only runs for speculators
16
+ userType: 'speculator',
26
17
  category: 'core_speculator'
27
18
  };
28
19
  }
29
20
 
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
- */
40
23
  static getSchema() {
41
24
  const tickerSchema = {
42
25
  "type": "object",
@@ -47,13 +30,7 @@ class SpeculatorTSLPerAsset {
47
30
  },
48
31
  "required": ["tsl_usage_pct", "tsl_count", "total_count"]
49
32
  };
50
-
51
- return {
52
- "type": "object",
53
- "description": "Calculates the percentage of speculators using a *trailing* stop-loss per asset.",
54
- "patternProperties": { "^.*$": tickerSchema },
55
- "additionalProperties": tickerSchema
56
- };
33
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
57
34
  }
58
35
 
59
36
  _initAsset(instrumentId) {
@@ -62,46 +39,32 @@ class SpeculatorTSLPerAsset {
62
39
  }
63
40
  }
64
41
 
65
- // --- STANDARD 0: UPDATED SIGNATURE ---
66
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
67
- // --- STANDARD 0: ADDED ---
68
- if (!this.tickerMap) {
69
- this.tickerMap = context.instrumentToTicker;
70
- }
42
+ process(context) {
43
+ const { extract } = context.math;
44
+ const { mappings, user } = context;
45
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
71
46
 
72
- const positions = todayPortfolio.PublicPositions;
73
- if (!positions || !Array.isArray(positions)) {
74
- return;
75
- }
47
+ const positions = extract.getPositions(user.portfolio.today, user.type);
76
48
 
77
49
  for (const pos of positions) {
78
- const instrumentId = pos.InstrumentID;
79
- if (!instrumentId) continue;
50
+ const instId = extract.getInstrumentId(pos);
51
+ if (!instId) continue;
80
52
 
81
- this._initAsset(instrumentId);
82
- const assetData = this.assets.get(instrumentId);
53
+ this._initAsset(instId);
54
+ const assetData = this.assets.get(instId);
83
55
  assetData.total++;
84
56
 
85
- // HasTrailingStopLoss is a boolean
86
- if (pos.HasTrailingStopLoss === true) {
57
+ if (extract.getHasTSL(pos)) {
87
58
  assetData.with_tsl++;
88
59
  }
89
60
  }
90
61
  }
91
62
 
92
63
  async getResult() {
93
- // --- STANDARD 0: REMOVED forbidden data load ---
94
-
95
- // Failsafe check
96
- if (!this.tickerMap) {
97
- return {}; // process() must run first
98
- }
99
-
64
+ if (!this.tickerMap) return {};
100
65
  const result = {};
101
- for (const [instrumentId, data] of this.assets.entries()) {
102
- // --- STANDARD 0: SIMPLIFIED ---
103
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
104
-
66
+ for (const [instId, data] of this.assets.entries()) {
67
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
105
68
  if (data.total > 0) {
106
69
  result[ticker] = {
107
70
  tsl_usage_pct: (data.with_tsl / data.total) * 100,
@@ -115,9 +78,7 @@ class SpeculatorTSLPerAsset {
115
78
 
116
79
  reset() {
117
80
  this.assets.clear();
118
- // --- STANDARD 0: RENAMED ---
119
81
  this.tickerMap = null;
120
82
  }
121
83
  }
122
-
123
84
  module.exports = SpeculatorTSLPerAsset;
@@ -1,18 +1,13 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for total long figures.
3
- *
4
- * This metric answers: "What is the total $USD value
5
- * and total count of all long ('buy') positions?"
3
+ * REFACTORED: Tracks exposure weight (percentage of equity), not USD.
6
4
  */
7
5
  class TotalLongFigures {
8
6
  constructor() {
9
- this.totalInvestedUsd = 0;
7
+ this.totalExposureWeight = 0;
10
8
  this.totalPositions = 0;
11
9
  }
12
10
 
13
- /**
14
- * Statically defines all metadata for the manifest builder.
15
- */
16
11
  static getMetadata() {
17
12
  return {
18
13
  type: 'standard',
@@ -23,40 +18,30 @@ class TotalLongFigures {
23
18
  };
24
19
  }
25
20
 
26
- /**
27
- * Statically declare dependencies.
28
- */
29
- static getDependencies() {
30
- return [];
31
- }
21
+ static getDependencies() { return []; }
32
22
 
33
- /**
34
- * Defines the output schema for this calculation.
35
- */
36
23
  static getSchema() {
37
24
  return {
38
25
  "type": "object",
39
- "description": "Aggregates the total $USD value and count of all long positions.",
40
26
  "properties": {
41
- "total_invested_usd": { "type": "number" },
27
+ "total_long_exposure_weight": { "type": "number", "description": "Sum of portfolio allocation percentages for all long positions." },
42
28
  "total_positions_count": { "type": "number" }
43
29
  },
44
- "required": ["total_invested_usd", "total_positions_count"]
30
+ "required": ["total_long_exposure_weight", "total_positions_count"]
45
31
  };
46
32
  }
47
33
 
48
- // --- UPDATED SIGNATURE ---
49
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
50
- // --- UPDATED ---
51
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
52
- if (!positions || !Array.isArray(positions)) {
53
- return;
54
- }
34
+ process(context) {
35
+ const { extract } = context.math;
36
+ const { user } = context;
37
+
38
+ const positions = extract.getPositions(user.portfolio.today, user.type);
55
39
 
56
40
  for (const pos of positions) {
57
- // Only count 'buy' (long) positions
58
- if (pos.IsBuy) {
59
- this.totalInvestedUsd += pos.Invested || 0;
41
+ const direction = extract.getDirection(pos);
42
+
43
+ if (direction === 'Buy') {
44
+ this.totalExposureWeight += extract.getPositionWeight(pos, user.type);
60
45
  this.totalPositions++;
61
46
  }
62
47
  }
@@ -64,13 +49,13 @@ class TotalLongFigures {
64
49
 
65
50
  getResult() {
66
51
  return {
67
- total_invested_usd: this.totalInvestedUsd,
52
+ total_long_exposure_weight: this.totalExposureWeight,
68
53
  total_positions_count: this.totalPositions
69
54
  };
70
55
  }
71
56
 
72
57
  reset() {
73
- this.totalInvestedUsd = 0;
58
+ this.totalExposureWeight = 0;
74
59
  this.totalPositions = 0;
75
60
  }
76
61
  }
@@ -1,22 +1,13 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for total long value per sector.
3
- *
4
- * This metric answers: "What is the total $USD value
5
- * of all long ('buy') positions, grouped by sector?"
2
+ * @fileoverview Calculation (Pass 1) for long figures per sector.
3
+ * REFACTORED: Tracks exposure weight (percentage).
6
4
  */
7
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
-
9
5
  class TotalLongPerSector {
10
6
  constructor() {
11
- // { [sectorName]: { total_invested_usd: 0, total_positions_count: 0 } }
12
- this.sectors = new Map();
13
- // --- STANDARD 0: RENAMED ---
7
+ this.sectorData = new Map();
14
8
  this.sectorMap = null;
15
9
  }
16
10
 
17
- /**
18
- * Statically defines all metadata for the manifest builder.
19
- */
20
11
  static getMetadata() {
21
12
  return {
22
13
  type: 'standard',
@@ -27,78 +18,65 @@ class TotalLongPerSector {
27
18
  };
28
19
  }
29
20
 
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
- */
40
23
  static getSchema() {
41
- const sectorSchema = {
24
+ const schema = {
42
25
  "type": "object",
43
26
  "properties": {
44
- "total_invested_usd": { "type": "number" },
27
+ "total_long_exposure_weight": { "type": "number" },
45
28
  "total_positions_count": { "type": "number" }
46
- }
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
29
+ },
30
+ "required": ["total_long_exposure_weight", "total_positions_count"]
54
31
  };
32
+ return { "type": "object", "patternProperties": { "^.*$": schema } };
55
33
  }
56
34
 
57
- _initSector(sectorName) {
58
- if (!this.sectors.has(sectorName)) {
59
- this.sectors.set(sectorName, { total_invested_usd: 0, total_positions_count: 0 });
35
+ _initSector(sector) {
36
+ if (!this.sectorData.has(sector)) {
37
+ this.sectorData.set(sector, { weight: 0, count: 0 });
60
38
  }
61
39
  }
62
40
 
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;
68
- }
41
+ process(context) {
42
+ const { extract } = context.math;
43
+ const { mappings, user } = context;
44
+ if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
69
45
 
70
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
71
- if (!positions || !Array.isArray(positions) || !this.sectorMap) {
72
- return;
73
- }
46
+ const positions = extract.getPositions(user.portfolio.today, user.type);
74
47
 
75
48
  for (const pos of positions) {
76
- // Only count 'buy' (long) positions
77
- if (pos.IsBuy) {
78
- const instrumentId = pos.InstrumentID;
79
- if (!instrumentId) continue;
80
-
81
- // --- STANDARD 0: FIXED ---
82
- const sectorName = this.sectorMap[instrumentId] || 'N/A';
83
- this._initSector(sectorName);
84
- const sectorData = this.sectors.get(sectorName);
49
+ if (extract.getDirection(pos) !== 'Buy') continue;
85
50
 
86
- sectorData.total_invested_usd += pos.Invested || 0;
87
- sectorData.total_positions_count++;
88
- }
51
+ const instId = extract.getInstrumentId(pos);
52
+ if (!instId) continue;
53
+
54
+ const sector = this.sectorMap[instId] || 'Unknown';
55
+ const weight = extract.getPositionWeight(pos, user.type);
56
+
57
+ this._initSector(sector);
58
+ const data = this.sectorData.get(sector);
59
+ data.weight += weight;
60
+ data.count++;
89
61
  }
90
62
  }
91
63
 
92
64
  async getResult() {
93
- // --- STANDARD 0: REMOVED forbidden data load ---
94
- return Object.fromEntries(this.sectors);
65
+ const result = {};
66
+ for (const [sector, data] of this.sectorData.entries()) {
67
+ if (data.count > 0) {
68
+ result[sector] = {
69
+ total_long_exposure_weight: data.weight,
70
+ total_positions_count: data.count
71
+ };
72
+ }
73
+ }
74
+ return result;
95
75
  }
96
76
 
97
77
  reset() {
98
- this.sectors.clear();
99
- // --- STANDARD 0: RENAMED ---
78
+ this.sectorData.clear();
100
79
  this.sectorMap = null;
101
80
  }
102
81
  }
103
-
104
82
  module.exports = TotalLongPerSector;
@@ -1,18 +1,13 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for total short figures.
3
- *
4
- * This metric answers: "What is the total $USD value
5
- * and total count of all short ('sell') positions?"
3
+ * REFACTORED: Tracks exposure weight (percentage).
6
4
  */
7
5
  class TotalShortFigures {
8
6
  constructor() {
9
- this.totalInvestedUsd = 0;
7
+ this.totalExposureWeight = 0;
10
8
  this.totalPositions = 0;
11
9
  }
12
10
 
13
- /**
14
- * Statically defines all metadata for the manifest builder.
15
- */
16
11
  static getMetadata() {
17
12
  return {
18
13
  type: 'standard',
@@ -23,40 +18,27 @@ class TotalShortFigures {
23
18
  };
24
19
  }
25
20
 
26
- /**
27
- * Statically declare dependencies.
28
- */
29
- static getDependencies() {
30
- return [];
31
- }
21
+ static getDependencies() { return []; }
32
22
 
33
- /**
34
- * Defines the output schema for this calculation.
35
- */
36
23
  static getSchema() {
37
24
  return {
38
25
  "type": "object",
39
- "description": "Aggregates the total $USD value and count of all short positions.",
40
26
  "properties": {
41
- "total_invested_usd": { "type": "number" },
27
+ "total_short_exposure_weight": { "type": "number" },
42
28
  "total_positions_count": { "type": "number" }
43
29
  },
44
- "required": ["total_invested_usd", "total_positions_count"]
30
+ "required": ["total_short_exposure_weight", "total_positions_count"]
45
31
  };
46
32
  }
47
33
 
48
- // --- UPDATED SIGNATURE ---
49
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
50
- // --- UPDATED ---
51
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
52
- if (!positions || !Array.isArray(positions)) {
53
- return;
54
- }
34
+ process(context) {
35
+ const { extract } = context.math;
36
+ const { user } = context;
37
+ const positions = extract.getPositions(user.portfolio.today, user.type);
55
38
 
56
39
  for (const pos of positions) {
57
- // Only count 'sell' (short) positions
58
- if (!pos.IsBuy) {
59
- this.totalInvestedUsd += pos.Invested || 0;
40
+ if (extract.getDirection(pos) === 'Sell') {
41
+ this.totalExposureWeight += extract.getPositionWeight(pos, user.type);
60
42
  this.totalPositions++;
61
43
  }
62
44
  }
@@ -64,15 +46,14 @@ class TotalShortFigures {
64
46
 
65
47
  getResult() {
66
48
  return {
67
- total_invested_usd: this.totalInvestedUsd,
49
+ total_short_exposure_weight: this.totalExposureWeight,
68
50
  total_positions_count: this.totalPositions
69
51
  };
70
52
  }
71
53
 
72
54
  reset() {
73
- this.totalInvestedUsd = 0;
55
+ this.totalExposureWeight = 0;
74
56
  this.totalPositions = 0;
75
57
  }
76
58
  }
77
-
78
59
  module.exports = TotalShortFigures;
@@ -1,22 +1,13 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for total short value per sector.
3
- *
4
- * This metric answers: "What is the total $USD value
5
- * of all short ('sell') positions, grouped by sector?"
2
+ * @fileoverview Calculation (Pass 1) for short figures per sector.
3
+ * REFACTORED: Tracks exposure weight (percentage).
6
4
  */
7
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
-
9
5
  class TotalShortPerSector {
10
6
  constructor() {
11
- // { [sectorName]: { total_invested_usd: 0, total_positions_count: 0 } }
12
- this.sectors = new Map();
13
- // --- STANDARD 0: RENAMED ---
7
+ this.sectorData = new Map();
14
8
  this.sectorMap = null;
15
9
  }
16
10
 
17
- /**
18
- * Statically defines all metadata for the manifest builder.
19
- */
20
11
  static getMetadata() {
21
12
  return {
22
13
  type: 'standard',
@@ -27,78 +18,65 @@ class TotalShortPerSector {
27
18
  };
28
19
  }
29
20
 
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
- */
40
23
  static getSchema() {
41
- const sectorSchema = {
24
+ const schema = {
42
25
  "type": "object",
43
26
  "properties": {
44
- "total_invested_usd": { "type": "number" },
27
+ "total_short_exposure_weight": { "type": "number" },
45
28
  "total_positions_count": { "type": "number" }
46
- }
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
29
+ },
30
+ "required": ["total_short_exposure_weight", "total_positions_count"]
54
31
  };
32
+ return { "type": "object", "patternProperties": { "^.*$": schema } };
55
33
  }
56
34
 
57
- _initSector(sectorName) {
58
- if (!this.sectors.has(sectorName)) {
59
- this.sectors.set(sectorName, { total_invested_usd: 0, total_positions_count: 0 });
35
+ _initSector(sector) {
36
+ if (!this.sectorData.has(sector)) {
37
+ this.sectorData.set(sector, { weight: 0, count: 0 });
60
38
  }
61
39
  }
62
40
 
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;
68
- }
41
+ process(context) {
42
+ const { extract } = context.math;
43
+ const { mappings, user } = context;
44
+ if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
69
45
 
70
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
71
- if (!positions || !Array.isArray(positions) || !this.sectorMap) {
72
- return;
73
- }
46
+ const positions = extract.getPositions(user.portfolio.today, user.type);
74
47
 
75
48
  for (const pos of positions) {
76
- // Only count 'sell' (short) positions
77
- if (!pos.IsBuy) {
78
- const instrumentId = pos.InstrumentID;
79
- if (!instrumentId) continue;
80
-
81
- // --- STANDARD 0: FIXED ---
82
- const sectorName = this.sectorMap[instrumentId] || 'N/A';
83
- this._initSector(sectorName);
84
- const sectorData = this.sectors.get(sectorName);
49
+ if (extract.getDirection(pos) !== 'Sell') continue;
85
50
 
86
- sectorData.total_invested_usd += pos.Invested || 0;
87
- sectorData.total_positions_count++;
88
- }
51
+ const instId = extract.getInstrumentId(pos);
52
+ if (!instId) continue;
53
+
54
+ const sector = this.sectorMap[instId] || 'Unknown';
55
+ const weight = extract.getPositionWeight(pos, user.type);
56
+
57
+ this._initSector(sector);
58
+ const data = this.sectorData.get(sector);
59
+ data.weight += weight;
60
+ data.count++;
89
61
  }
90
62
  }
91
63
 
92
64
  async getResult() {
93
- // --- STANDARD 0: REMOVED forbidden data load ---
94
- return Object.fromEntries(this.sectors);
65
+ const result = {};
66
+ for (const [sector, data] of this.sectorData.entries()) {
67
+ if (data.count > 0) {
68
+ result[sector] = {
69
+ total_short_exposure_weight: data.weight,
70
+ total_positions_count: data.count
71
+ };
72
+ }
73
+ }
74
+ return result;
95
75
  }
96
76
 
97
77
  reset() {
98
- this.sectors.clear();
99
- // --- STANDARD 0: RENAMED ---
78
+ this.sectorData.clear();
100
79
  this.sectorMap = null;
101
80
  }
102
81
  }
103
-
104
82
  module.exports = TotalShortPerSector;