aiden-shared-calculations-unified 1.0.84 → 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,22 +1,13 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for profitability ratio per stock.
3
- *
4
- * This metric answers: "For each stock, what is the count of
5
- * profitable versus unprofitable positions?"
3
+ * REFACTORED: Counts (No USD involved).
6
4
  */
7
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
-
9
5
  class ProfitabilityRatioPerStock {
10
6
  constructor() {
11
- // We will store { [instrumentId]: { profitable: 0, unprofitable: 0 } }
12
- this.assets = new Map();
13
- // --- STANDARD 0: RENAMED ---
7
+ this.stockData = new Map();
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',
@@ -27,102 +18,57 @@ class ProfitabilityRatioPerStock {
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 tickerSchema = {
24
+ const stockSchema = {
42
25
  "type": "object",
43
- "description": "Profit/loss position counts for a specific asset.",
44
26
  "properties": {
45
- "profitable_count": {
46
- "type": "number",
47
- "description": "Count of positions in profit."
48
- },
49
- "unprofitable_count": {
50
- "type": "number",
51
- "description": "Count of positions in loss."
52
- },
53
- "ratio": {
54
- "type": ["number", "null"],
55
- "description": "Ratio of profitable to unprofitable (Profit / Loss). Null if no losing positions."
56
- }
57
- },
58
- "required": ["profitable_count", "unprofitable_count", "ratio"]
59
- };
60
-
61
- return {
62
- "type": "object",
63
- "description": "Calculates the count of profitable vs. unprofitable positions for each asset.",
64
- "patternProperties": {
65
- "^.*$": tickerSchema // Ticker
27
+ "profitability_ratio": { "type": ["number", "null"] },
28
+ "profitable_count": { "type": "number" },
29
+ "unprofitable_count": { "type": "number" }
66
30
  },
67
- "additionalProperties": tickerSchema
31
+ "required": ["profitability_ratio", "profitable_count", "unprofitable_count"]
68
32
  };
33
+ return { "type": "object", "patternProperties": { "^.*$": stockSchema } };
69
34
  }
70
35
 
71
- _initAsset(instrumentId) {
72
- if (!this.assets.has(instrumentId)) {
73
- this.assets.set(instrumentId, {
74
- profitable: 0,
75
- unprofitable: 0
76
- });
36
+ _initStock(instId) {
37
+ if (!this.stockData.has(instId)) {
38
+ this.stockData.set(instId, { profitable: 0, unprofitable: 0 });
77
39
  }
78
40
  }
79
41
 
80
- // --- STANDARD 0: UPDATED SIGNATURE ---
81
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
82
- // --- STANDARD 0: ADDED ---
83
- if (!this.tickerMap) {
84
- this.tickerMap = context.instrumentToTicker;
85
- }
86
-
87
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
88
- if (!positions || !Array.isArray(positions)) {
89
- return;
90
- }
42
+ process(context) {
43
+ const { extract } = context.math;
44
+ const { mappings, user } = context;
45
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
46
+
47
+ const positions = extract.getPositions(user.portfolio.today, user.type);
91
48
 
92
49
  for (const pos of positions) {
93
- const instrumentId = pos.InstrumentID;
94
- if (!instrumentId) continue;
95
-
96
- this._initAsset(instrumentId);
97
- const assetData = this.assets.get(instrumentId);
98
- const pnl = pos.NetProfit || 0;
50
+ const instId = extract.getInstrumentId(pos);
51
+ if (!instId) continue;
99
52
 
100
- if (pnl > 0) {
101
- assetData.profitable++;
102
- } else if (pnl < 0) {
103
- assetData.unprofitable++;
104
- }
53
+ const pnl = extract.getNetProfit(pos);
54
+ this._initStock(instId);
55
+ const data = this.stockData.get(instId);
56
+
57
+ if (pnl > 0) data.profitable++;
58
+ else if (pnl < 0) data.unprofitable++;
105
59
  }
106
60
  }
107
61
 
108
62
  async getResult() {
109
- // --- STANDARD 0: REMOVED forbidden data load ---
110
-
111
- // Failsafe check
112
- if (!this.tickerMap) {
113
- return {}; // process() must run first
114
- }
115
-
63
+ if (!this.tickerMap) return {};
116
64
  const result = {};
117
- for (const [instrumentId, data] of this.assets.entries()) {
118
- // --- STANDARD 0: SIMPLIFIED ---
119
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
120
-
65
+ for (const [instId, data] of this.stockData.entries()) {
66
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
121
67
  if (data.profitable > 0 || data.unprofitable > 0) {
122
68
  result[ticker] = {
69
+ profitability_ratio: (data.unprofitable > 0) ? (data.profitable / data.unprofitable) : null,
123
70
  profitable_count: data.profitable,
124
- unprofitable_count: data.unprofitable,
125
- ratio: (data.unprofitable > 0) ? (data.profitable / data.unprofitable) : null
71
+ unprofitable_count: data.unprofitable
126
72
  };
127
73
  }
128
74
  }
@@ -130,10 +76,8 @@ class ProfitabilityRatioPerStock {
130
76
  }
131
77
 
132
78
  reset() {
133
- this.assets.clear();
134
- // --- STANDARD 0: RENAMED ---
79
+ this.stockData.clear();
135
80
  this.tickerMap = null;
136
81
  }
137
82
  }
138
-
139
83
  module.exports = ProfitabilityRatioPerStock;
@@ -1,25 +1,13 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for profitability skew.
3
- *
4
- _ This metric answers: "For each stock, what is the sum and
5
- * count of *profits* and *losses*?"
6
- *
7
- * This helps determine if returns are skewed (e.g., many small
8
- * wins and a few huge losses).
2
+ * @fileoverview Calculation (Pass 1) for profitability skew per stock.
3
+ * REFACTORED: Uses percentages.
9
4
  */
10
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
11
-
12
5
  class ProfitabilitySkewPerStock {
13
6
  constructor() {
14
- // { [instrumentId]: { profit_sum: 0, profit_count: 0, loss_sum: 0, loss_count: 0 } }
15
- this.assets = new Map();
16
- // --- STANDARD 0: RENAMED ---
7
+ this.stockData = new Map();
17
8
  this.tickerMap = null;
18
9
  }
19
10
 
20
- /**
21
- * Statically defines all metadata for the manifest builder.
22
- */
23
11
  static getMetadata() {
24
12
  return {
25
13
  type: 'standard',
@@ -30,116 +18,75 @@ class ProfitabilitySkewPerStock {
30
18
  };
31
19
  }
32
20
 
33
- /**
34
- * Statically declare dependencies.
35
- */
36
- static getDependencies() {
37
- return [];
38
- }
21
+ static getDependencies() { return []; }
39
22
 
40
- /**
41
- * Defines the output schema for this calculation.
42
- */
43
23
  static getSchema() {
44
- const tickerSchema = {
24
+ const stockSchema = {
45
25
  "type": "object",
46
- "description": "P&L skew metrics for a specific asset.",
47
26
  "properties": {
48
- "profit_sum": { "type": "number" },
49
- "profit_count": { "type": "number" },
50
- "loss_sum": { "type": "number" },
51
- "loss_count": { "type": "number" },
52
- "skew_ratio": {
53
- "type": ["number", "null"],
54
- "description": "Ratio of (Average Profit / Average Loss). Null if no losses."
55
- }
27
+ "avg_profit_pct": { "type": "number" },
28
+ "avg_loss_pct": { "type": "number" },
29
+ "skew_ratio": { "type": ["number", "null"] }
56
30
  },
57
- "required": ["profit_sum", "profit_count", "loss_sum", "loss_count", "skew_ratio"]
58
- };
59
-
60
- return {
61
- "type": "object",
62
- "description": "Calculates the skew of returns (avg profit vs. avg loss) for each asset.",
63
- "patternProperties": {
64
- "^.*$": tickerSchema // Ticker
65
- },
66
- "additionalProperties": tickerSchema
31
+ "required": ["avg_profit_pct", "avg_loss_pct", "skew_ratio"]
67
32
  };
33
+ return { "type": "object", "patternProperties": { "^.*$": stockSchema } };
68
34
  }
69
35
 
70
- _initAsset(instrumentId) {
71
- if (!this.assets.has(instrumentId)) {
72
- this.assets.set(instrumentId, {
73
- profit_sum: 0,
74
- profit_count: 0,
75
- loss_sum: 0, // Will be negative
76
- loss_count: 0
77
- });
36
+ _initStock(instId) {
37
+ if (!this.stockData.has(instId)) {
38
+ this.stockData.set(instId, { profitSumPct: 0, profitCount: 0, lossSumPct: 0, lossCount: 0 });
78
39
  }
79
40
  }
80
41
 
81
- // --- STANDARD 0: UPDATED SIGNATURE ---
82
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
83
- // --- STANDARD 0: ADDED ---
84
- if (!this.tickerMap) {
85
- this.tickerMap = context.instrumentToTicker;
86
- }
42
+ process(context) {
43
+ const { extract } = context.math;
44
+ const { mappings, user } = context;
45
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
87
46
 
88
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
89
- if (!positions || !Array.isArray(positions)) {
90
- return;
91
- }
47
+ const positions = extract.getPositions(user.portfolio.today, user.type);
92
48
 
93
49
  for (const pos of positions) {
94
- const instrumentId = pos.InstrumentID;
95
- if (!instrumentId) continue;
96
-
97
- this._initAsset(instrumentId);
98
- const assetData = this.assets.get(instrumentId);
99
- const pnl = pos.NetProfit || 0;
50
+ const instId = extract.getInstrumentId(pos);
51
+ if (!instId) continue;
52
+
53
+ const pnl = extract.getNetProfit(pos);
54
+ if (pnl === 0) continue;
55
+
56
+ this._initStock(instId);
57
+ const data = this.stockData.get(instId);
100
58
 
101
59
  if (pnl > 0) {
102
- assetData.profit_sum += pnl;
103
- assetData.profit_count++;
104
- } else if (pnl < 0) {
105
- assetData.loss_sum += pnl;
106
- assetData.loss_count++;
60
+ data.profitSumPct += pnl;
61
+ data.profitCount++;
62
+ } else {
63
+ data.lossSumPct += Math.abs(pnl);
64
+ data.lossCount++;
107
65
  }
108
66
  }
109
67
  }
110
68
 
111
69
  async getResult() {
112
- // --- STANDARD 0: REMOVED forbidden data load ---
113
-
114
- // Failsafe check
115
- if (!this.tickerMap) {
116
- return {}; // process() must run first
117
- }
118
-
70
+ if (!this.tickerMap) return {};
119
71
  const result = {};
120
- for (const [instrumentId, data] of this.assets.entries()) {
121
- // --- STANDARD 0: SIMPLIFIED ---
122
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
72
+ for (const [instId, data] of this.stockData.entries()) {
73
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
74
+
75
+ const avgProfit = data.profitCount > 0 ? data.profitSumPct / data.profitCount : 0;
76
+ const avgLoss = data.lossCount > 0 ? data.lossSumPct / data.lossCount : 0;
123
77
 
124
- const avgProfit = (data.profit_count > 0) ? (data.profit_sum / data.profit_count) : 0;
125
- const avgLoss = (data.loss_count > 0) ? (Math.abs(data.loss_sum) / data.loss_count) : 0;
126
-
127
78
  result[ticker] = {
128
- profit_sum: data.profit_sum,
129
- profit_count: data.profit_count,
130
- loss_sum: data.loss_sum,
131
- loss_count: data.loss_count,
132
- skew_ratio: (avgLoss > 0) ? (avgProfit / avgLoss) : null
79
+ avg_profit_pct: avgProfit,
80
+ avg_loss_pct: avgLoss,
81
+ skew_ratio: avgLoss > 0 ? avgProfit / avgLoss : null
133
82
  };
134
83
  }
135
84
  return result;
136
85
  }
137
86
 
138
87
  reset() {
139
- this.assets.clear();
140
- // --- STANDARD 0: RENAMED ---
88
+ this.stockData.clear();
141
89
  this.tickerMap = null;
142
90
  }
143
91
  }
144
-
145
92
  module.exports = ProfitabilitySkewPerStock;
@@ -1,113 +1,81 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for overall profitable status.
3
- *
4
- * This metric answers: "How many users have an *overall portfolio*
5
- * in profit versus in loss today?"
2
+ * @fileoverview Calculation (Pass 1) - Bucket users by P&L status.
3
+ * REFACTORED: Standardized extraction.
6
4
  */
7
5
  class ProfitableAndUnprofitableStatus {
8
6
  constructor() {
9
- this.total_in_profit = 0;
10
- this.total_in_loss = 0;
7
+ this.stockData = new Map();
8
+ this.tickerMap = null;
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',
19
- rootDataDependencies: ['portfolio'], // Needs portfolio.Summary
14
+ rootDataDependencies: ['portfolio'],
20
15
  isHistorical: false,
21
16
  userType: 'all',
22
17
  category: 'core_pnl'
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
- return {
24
+ const schema = {
38
25
  "type": "object",
39
- "description": "Tracks the count of users whose *overall portfolio* is in profit vs. in loss.",
40
26
  "properties": {
41
- "total_in_profit": {
42
- "type": "number",
43
- "description": "Count of users with a total portfolio P&L > 0."
44
- },
45
- "total_in_loss": {
46
- "type": "number",
47
- "description": "Count of users with a total portfolio P&L < 0."
48
- },
49
- "profit_ratio_pct": {
50
- "type": "number",
51
- "description": "Percentage of users in profit (In Profit / Total)."
52
- }
53
- },
54
- "required": ["total_in_profit", "total_in_loss", "profit_ratio_pct"]
27
+ "profitable_users": { "type": "array", "items": { "type": "string" } },
28
+ "unprofitable_users": { "type": "array", "items": { "type": "string" } }
29
+ }
55
30
  };
31
+ return { "type": "object", "patternProperties": { "^.*$": schema } };
56
32
  }
57
33
 
58
- // --- UPDATED SIGNATURE ---
59
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
60
-
61
- // ---
62
- // FIX: The 'data-generator.js' does not provide a 'Summary' object
63
- // or a root-level 'NetProfit' field.
64
- // We must derive the overall P&L from the individual positions,
65
- // which *are* generated.
66
- // ---
67
- const positions = todayPortfolio.AggregatedPositions;
68
- if (!positions || !Array.isArray(positions) || positions.length === 0) {
69
- return; // No positions, no P&L status
34
+ _initStock(instId) {
35
+ if (!this.stockData.has(instId)) {
36
+ this.stockData.set(instId, { profitable: [], unprofitable: [] });
70
37
  }
38
+ }
71
39
 
72
- let totalInvested = 0;
73
- let weightedPnlSum = 0;
40
+ process(context) {
41
+ const { extract } = context.math;
42
+ const { mappings, user } = context;
43
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
74
44
 
75
- for (const pos of positions) {
76
- const invested = pos.Invested;
77
- const pnl = pos.NetProfit;
45
+ const positions = extract.getPositions(user.portfolio.today, user.type);
78
46
 
79
- if (typeof invested === 'number' && typeof pnl === 'number') {
80
- totalInvested += invested;
81
- weightedPnlSum += (pnl * invested); // Weight the P&L by its size
82
- }
83
- }
47
+ for (const pos of positions) {
48
+ const instId = extract.getInstrumentId(pos);
49
+ const pnl = extract.getNetProfit(pos);
50
+
51
+ if (!instId || pnl === 0) continue;
84
52
 
85
- if (totalInvested === 0) {
86
- return; // No invested capital, no P&L status
87
- }
53
+ this._initStock(instId);
54
+ const data = this.stockData.get(instId);
88
55
 
89
- const overallPnl = weightedPnlSum / totalInvested;
90
-
91
- if (overallPnl > 0) {
92
- this.total_in_profit++;
93
- } else if (overallPnl < 0) {
94
- this.total_in_loss++;
56
+ if (pnl > 0) data.profitable.push(user.id);
57
+ else data.unprofitable.push(user.id);
95
58
  }
96
59
  }
97
60
 
98
- getResult() {
99
- const total = this.total_in_profit + this.total_in_loss;
100
- return {
101
- total_in_profit: this.total_in_profit,
102
- total_in_loss: this.total_in_loss,
103
- profit_ratio_pct: (total > 0) ? (this.total_in_profit / total) * 100 : 0
104
- };
61
+ async getResult() {
62
+ if (!this.tickerMap) return {};
63
+ const result = {};
64
+ for (const [instId, data] of this.stockData.entries()) {
65
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
66
+ if (data.profitable.length > 0 || data.unprofitable.length > 0) {
67
+ result[ticker] = {
68
+ profitable_users: data.profitable,
69
+ unprofitable_users: data.unprofitable
70
+ };
71
+ }
72
+ }
73
+ return result;
105
74
  }
106
75
 
107
76
  reset() {
108
- this.total_in_profit = 0;
109
- this.total_in_loss = 0;
77
+ this.stockData.clear();
78
+ this.tickerMap = null;
110
79
  }
111
80
  }
112
-
113
81
  module.exports = ProfitableAndUnprofitableStatus;
@@ -1,56 +1,26 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for sentiment per stock.
3
- *
4
- * This metric answers: "For each stock, what is the count
5
- * of long versus short positions?"
3
+ * REFACTORED: Counts Long vs Short.
6
4
  */
7
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
-
9
5
  class SentimentPerStock {
10
6
  constructor() {
11
- // { [instrumentId]: { long: 0, short: 0 } }
12
7
  this.assets = new Map();
13
- // --- STANDARD 0: RENAMED ---
14
8
  this.tickerMap = null;
15
9
  }
16
10
 
17
- /**
18
- * Defines the output schema for this calculation.
19
- */
20
11
  static getSchema() {
21
12
  const tickerSchema = {
22
13
  "type": "object",
23
- "description": "Long/short position counts for a specific asset.",
24
14
  "properties": {
25
- "long_count": {
26
- "type": "number",
27
- "description": "Count of long ('buy') positions."
28
- },
29
- "short_count": {
30
- "type": "number",
31
- "description": "Count of short ('sell') positions."
32
- },
33
- "sentiment_ratio": {
34
- "type": ["number", "null"],
35
- "description": "Ratio of long to short (Long / Short). Null if no short positions."
36
- }
15
+ "long_count": { "type": "number" },
16
+ "short_count": { "type": "number" },
17
+ "sentiment_ratio": { "type": ["number", "null"] }
37
18
  },
38
19
  "required": ["long_count", "short_count", "sentiment_ratio"]
39
20
  };
40
-
41
- return {
42
- "type": "object",
43
- "description": "Calculates the count of long vs. short positions for each asset.",
44
- "patternProperties": {
45
- "^.*$": tickerSchema // Ticker
46
- },
47
- "additionalProperties": tickerSchema
48
- };
21
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
49
22
  }
50
23
 
51
- /**
52
- * Statically defines all metadata for the manifest builder.
53
- */
54
24
  static getMetadata() {
55
25
  return {
56
26
  type: 'standard',
@@ -61,62 +31,41 @@ class SentimentPerStock {
61
31
  };
62
32
  }
63
33
 
64
- /**
65
- * Statically declare dependencies.
66
- */
67
- static getDependencies() {
68
- return [];
69
- }
34
+ static getDependencies() { return []; }
70
35
 
71
36
  _initAsset(instrumentId) {
72
37
  if (!this.assets.has(instrumentId)) {
73
- this.assets.set(instrumentId, {
74
- long: 0,
75
- short: 0
76
- });
38
+ this.assets.set(instrumentId, { long: 0, short: 0 });
77
39
  }
78
40
  }
79
41
 
80
- // --- STANDARD 0: UPDATED SIGNATURE ---
81
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
82
- // --- STANDARD 0: ADDED ---
83
- if (!this.tickerMap) {
84
- this.tickerMap = context.instrumentToTicker;
85
- }
42
+ process(context) {
43
+ const { extract } = context.math;
44
+ const { mappings, user } = context;
45
+ // Capture mapping reference for getResult
46
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
86
47
 
87
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
88
- if (!positions || !Array.isArray(positions)) {
89
- return;
90
- }
48
+ const positions = extract.getPositions(user.portfolio.today, user.type);
91
49
 
92
50
  for (const pos of positions) {
93
- const instrumentId = pos.InstrumentID;
94
- if (!instrumentId) continue;
51
+ const instId = extract.getInstrumentId(pos);
52
+ if (!instId) continue;
95
53
 
96
- this._initAsset(instrumentId);
97
- const assetData = this.assets.get(instrumentId);
54
+ this._initAsset(instId);
55
+ const assetData = this.assets.get(instId);
56
+ const direction = extract.getDirection(pos);
98
57
 
99
- if (pos.IsBuy) {
100
- assetData.long++;
101
- } else {
102
- assetData.short++;
103
- }
58
+ if (direction === 'Buy') assetData.long++;
59
+ else assetData.short++;
104
60
  }
105
61
  }
106
62
 
107
63
  async getResult() {
108
- // --- STANDARD 0: REMOVED forbidden data load ---
109
-
110
- // Failsafe check
111
- if (!this.tickerMap) {
112
- return {}; // process() must run first
113
- }
114
-
64
+ if (!this.tickerMap) return {};
65
+
115
66
  const result = {};
116
- for (const [instrumentId, data] of this.assets.entries()) {
117
- // --- STANDARD 0: SIMPLIFIED ---
118
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
119
-
67
+ for (const [instId, data] of this.assets.entries()) {
68
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
120
69
  if (data.long > 0 || data.short > 0) {
121
70
  result[ticker] = {
122
71
  long_count: data.long,
@@ -130,9 +79,7 @@ class SentimentPerStock {
130
79
 
131
80
  reset() {
132
81
  this.assets.clear();
133
- // --- STANDARD 0: RENAMED ---
134
82
  this.tickerMap = null;
135
83
  }
136
84
  }
137
-
138
85
  module.exports = SentimentPerStock;