aiden-shared-calculations-unified 1.0.86 → 1.0.88

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 (80) hide show
  1. package/calculations/capitulation/asset-volatility-estimator.js +96 -0
  2. package/calculations/capitulation/retail-capitulation-risk-forecast.js +173 -0
  3. package/calculations/core/asset-cost-basis-profile.js +127 -0
  4. package/calculations/core/asset-pnl-status.js +36 -106
  5. package/calculations/core/asset-position-size.js +40 -91
  6. package/calculations/core/average-daily-pnl-all-users.js +18 -57
  7. package/calculations/core/average-daily-pnl-per-sector.js +41 -88
  8. package/calculations/core/average-daily-pnl-per-stock.js +38 -91
  9. package/calculations/core/average-daily-position-pnl.js +19 -49
  10. package/calculations/core/holding-duration-per-asset.js +25 -127
  11. package/calculations/core/instrument-price-change-1d.js +30 -49
  12. package/calculations/core/instrument-price-momentum-20d.js +50 -60
  13. package/calculations/core/long-position-per-stock.js +39 -68
  14. package/calculations/core/overall-holding-duration.js +16 -87
  15. package/calculations/core/overall-profitability-ratio.js +11 -40
  16. package/calculations/core/platform-buy-sell-sentiment.js +41 -124
  17. package/calculations/core/platform-daily-bought-vs-sold-count.js +41 -99
  18. package/calculations/core/platform-daily-ownership-delta.js +68 -126
  19. package/calculations/core/platform-ownership-per-sector.js +45 -96
  20. package/calculations/core/platform-total-positions-held.js +20 -80
  21. package/calculations/core/pnl-distribution-per-stock.js +29 -135
  22. package/calculations/core/price-metrics.js +95 -206
  23. package/calculations/core/profitability-ratio-per-sector.js +34 -79
  24. package/calculations/core/profitability-ratio-per-stock.js +32 -88
  25. package/calculations/core/profitability-skew-per-stock.js +41 -94
  26. package/calculations/core/profitable-and-unprofitable-status.js +44 -76
  27. package/calculations/core/sentiment-per-stock.js +24 -77
  28. package/calculations/core/short-position-per-stock.js +35 -43
  29. package/calculations/core/social-activity-aggregation.js +26 -49
  30. package/calculations/core/social-asset-posts-trend.js +38 -94
  31. package/calculations/core/social-event-correlation.js +26 -93
  32. package/calculations/core/social-sentiment-aggregation.js +20 -44
  33. package/calculations/core/social-top-mentioned-words.js +35 -87
  34. package/calculations/core/social-topic-interest-evolution.js +22 -111
  35. package/calculations/core/social-topic-sentiment-matrix.js +38 -104
  36. package/calculations/core/social-word-mentions-trend.js +27 -104
  37. package/calculations/core/speculator-asset-sentiment.js +31 -72
  38. package/calculations/core/speculator-danger-zone.js +48 -84
  39. package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +20 -52
  40. package/calculations/core/speculator-distance-to-tp-per-leverage.js +23 -53
  41. package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +20 -50
  42. package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +23 -50
  43. package/calculations/core/speculator-leverage-per-asset.js +25 -64
  44. package/calculations/core/speculator-leverage-per-sector.js +27 -63
  45. package/calculations/core/speculator-risk-reward-ratio-per-asset.js +24 -53
  46. package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +55 -68
  47. package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +54 -71
  48. package/calculations/core/speculator-stop-loss-per-asset.js +19 -44
  49. package/calculations/core/speculator-take-profit-per-asset.js +20 -57
  50. package/calculations/core/speculator-tsl-per-asset.js +17 -56
  51. package/calculations/core/test..js +0 -0
  52. package/calculations/core/total-long-figures.js +16 -31
  53. package/calculations/core/total-long-per-sector.js +39 -61
  54. package/calculations/core/total-short-figures.js +13 -32
  55. package/calculations/core/total-short-per-sector.js +39 -61
  56. package/calculations/core/users-processed.js +11 -46
  57. package/calculations/gauss/cohort-capital-flow.js +54 -173
  58. package/calculations/gauss/cohort-definer.js +77 -163
  59. package/calculations/gauss/daily-dna-filter.js +29 -83
  60. package/calculations/gauss/gauss-divergence-signal.js +22 -109
  61. package/calculations/gem/cohort-momentum-state.js +27 -72
  62. package/calculations/gem/cohort-skill-definition.js +36 -52
  63. package/calculations/gem/platform-conviction-divergence.js +18 -60
  64. package/calculations/gem/quant-skill-alpha-signal.js +25 -98
  65. package/calculations/gem/skilled-cohort-flow.js +67 -175
  66. package/calculations/gem/skilled-unskilled-divergence.js +18 -73
  67. package/calculations/gem/unskilled-cohort-flow.js +64 -172
  68. package/calculations/ghost-book/cost-basis-density.js +79 -0
  69. package/calculations/ghost-book/liquidity-vacuum.js +52 -0
  70. package/calculations/ghost-book/retail-gamma-exposure.js +86 -0
  71. package/calculations/helix/helix-contrarian-signal.js +20 -114
  72. package/calculations/helix/herd-consensus-score.js +42 -124
  73. package/calculations/helix/winner-loser-flow.js +36 -118
  74. package/calculations/predicative-alpha/cognitive-dissonance.js +113 -0
  75. package/calculations/predicative-alpha/diamond-hand-fracture.js +90 -0
  76. package/calculations/predicative-alpha/mimetic-latency.js +124 -0
  77. package/calculations/pyro/risk-appetite-index.js +33 -74
  78. package/calculations/pyro/squeeze-potential.js +30 -87
  79. package/calculations/pyro/volatility-signal.js +33 -78
  80. package/package.json +1 -1
@@ -1,112 +1,83 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for long positions per stock.
3
- *
4
- * This metric answers: "How many long ('buy') positions
5
- * are there for each stock?"
3
+ * REFACTORED: Uses context.math.extract. No USD.
6
4
  */
7
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
-
9
5
  class LongPositionPerStock {
10
6
  constructor() {
11
- // We will store { [instrumentId]: count }
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',
23
14
  rootDataDependencies: ['portfolio'],
24
15
  isHistorical: false,
25
16
  userType: 'all',
26
- category: 'core_sentiment' // Based on Computation_documentation.md (short_and_long_stats -> sentiment_per_stock)
17
+ category: 'core_sentiment'
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
- return {
24
+ const schema = {
42
25
  "type": "object",
43
- "description": "Calculates the total count of long ('buy') positions for each asset.",
44
- "patternProperties": {
45
- // Ticker
46
- "^.*$": {
47
- "type": "number",
48
- "description": "The total count of long positions for this asset."
49
- }
26
+ "properties": {
27
+ "long_count": { "type": "number" },
28
+ "total_long_exposure_weight": { "type": "number" }
50
29
  },
51
- "additionalProperties": {
52
- "type": "number"
53
- }
30
+ "required": ["long_count", "total_long_exposure_weight"]
54
31
  };
32
+ return { "type": "object", "patternProperties": { "^.*$": schema } };
55
33
  }
56
34
 
57
- _initAsset(instrumentId) {
58
- if (!this.assets.has(instrumentId)) {
59
- this.assets.set(instrumentId, 0);
35
+ _initStock(instId) {
36
+ if (!this.stockData.has(instId)) {
37
+ this.stockData.set(instId, { count: 0, weight: 0 });
60
38
  }
61
39
  }
62
40
 
63
- // --- STANDARD 0: UPDATED SIGNATURE ---
64
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
65
- // --- STANDARD 0: ADDED ---
66
- if (!this.tickerMap) {
67
- this.tickerMap = context.instrumentToTicker;
68
- }
41
+ process(context) {
42
+ const { extract } = context.math;
43
+ const { mappings, user } = context;
44
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
69
45
 
70
- // --- STANDARD 0: UPDATED ---
71
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
72
- if (!positions || !Array.isArray(positions)) {
73
- return;
74
- }
46
+ const positions = extract.getPositions(user.portfolio.today, user.type);
75
47
 
76
48
  for (const pos of positions) {
77
- // Only count 'buy' (long) positions
78
- if (pos.IsBuy) {
79
- const instrumentId = pos.InstrumentID;
80
- if (!instrumentId) continue;
81
-
82
- this._initAsset(instrumentId);
83
- this.assets.set(instrumentId, this.assets.get(instrumentId) + 1);
84
- }
85
- }
86
- }
49
+ if (extract.getDirection(pos) !== 'Buy') continue;
87
50
 
88
- async getResult() {
89
- // --- STANDARD 0: REMOVED forbidden data load ---
51
+ const instId = extract.getInstrumentId(pos);
52
+ if (!instId) continue;
90
53
 
91
- // Failsafe check
92
- if (!this.tickerMap) {
93
- return {}; // process() must run first
54
+ const weight = extract.getPositionWeight(pos, user.type);
55
+
56
+ this._initStock(instId);
57
+ const data = this.stockData.get(instId);
58
+ data.count++;
59
+ data.weight += weight;
94
60
  }
61
+ }
95
62
 
63
+ async getResult() {
64
+ if (!this.tickerMap) return {};
96
65
  const result = {};
97
- for (const [instrumentId, count] of this.assets.entries()) {
98
- // --- STANDARD 0: SIMPLIFIED ---
99
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
100
- result[ticker] = count;
66
+ for (const [instId, data] of this.stockData.entries()) {
67
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
68
+ if (data.count > 0) {
69
+ result[ticker] = {
70
+ long_count: data.count,
71
+ total_long_exposure_weight: data.weight
72
+ };
73
+ }
101
74
  }
102
75
  return result;
103
76
  }
104
77
 
105
78
  reset() {
106
- this.assets.clear();
107
- // --- STANDARD 0: RENAMED ---
79
+ this.stockData.clear();
108
80
  this.tickerMap = null;
109
81
  }
110
82
  }
111
-
112
83
  module.exports = LongPositionPerStock;
@@ -1,108 +1,37 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for user behaviour.
3
- *
4
- * This metric answers: "What is the average holding duration (in hours)
5
- * for *closed* positions, averaged across all users?"
6
- *
7
- * It uses the 'history' data source's 'all' object.
3
+ * REFACTORED: Uses context.math.history.getDailyHistory().
8
4
  */
9
5
  class AverageHoldingDurationOverall {
10
- constructor() {
11
- // Stores all the user-level average durations
12
- this.durationsHours = [];
13
- }
6
+ constructor() { this.durationsHours = []; }
14
7
 
15
- // --- NEW ---
16
- /**
17
- * Statically defines all metadata for the manifest builder.
18
- */
19
8
  static getMetadata() {
20
- return {
21
- type: 'standard',
22
- rootDataDependencies: ['history'], // Needs the history doc
23
- isHistorical: false, // Needs today's history doc
24
- userType: 'all',
25
- category: 'core_metrics'
26
- };
27
- }
28
-
29
- // --- NEW ---
30
- /**
31
- * Statically declare dependencies.
32
- */
33
- static getDependencies() {
34
- return [];
9
+ return { type: 'standard', rootDataDependencies: ['history'], isHistorical: false, userType: 'all', category: 'core_metrics' };
35
10
  }
36
-
37
- /**
38
- * Defines the output schema for this calculation.
39
- * @returns {object} JSON Schema object
40
- */
11
+ static getDependencies() { return []; }
41
12
  static getSchema() {
42
- return {
43
- "type": "object",
44
- "description": "Calculates the platform-wide average holding duration (in hours) for closed positions.",
45
- "properties": {
46
- "average_duration_hours": {
47
- "type": "number",
48
- "description": "The average holding duration in hours, averaged across all users."
49
- },
50
- "user_count": {
51
- "type": "number",
52
- "description": "The number of users included in the average."
53
- }
54
- },
55
- "required": ["average_duration_hours", "user_count"]
56
- };
13
+ return { "type": "object", "properties": { "average_duration_hours": { "type": "number" }, "user_count": { "type": "number" } }, "required": ["average_duration_hours", "user_count"] };
57
14
  }
58
15
 
59
- // --- REFACTORED ---
60
- /**
61
- * Process data from the 'history' root data source.
62
- * @param {object} todayPortfolio - Contains today's history doc
63
- * @param {object} yesterdayPortfolio - (Not used)
64
- * @param {string} userId - The user ID.
65
- */
66
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
67
-
68
- // ---
69
- // FIX: The test harness injects data based on getMetadata().
70
- // Because rootDataDependencies is ONLY ['history'], the test harness
71
- // places the history object in the *first* argument (todayPortfolio).
72
- // ---
73
- const historyData = todayPortfolio; // NOT todayPortfolio?.history
74
- if (!historyData || !historyData.all) {
75
- return;
76
- }
16
+ process(context) {
17
+ const { history } = context.math;
18
+ const { user } = context;
77
19
 
78
- // 2. Get the user's overall average holding time
79
- const durationMinutes = historyData.all.avgHoldingTimeInMinutes;
20
+ // V4: Strict Access via Math Layer
21
+ const historyDoc = history.getDailyHistory(user);
22
+ const summary = history.getSummary(historyDoc);
80
23
 
81
- if (typeof durationMinutes === 'number' && durationMinutes > 0) {
82
- this.durationsHours.push(durationMinutes / 60);
24
+ if (summary && summary.avgHoldingTimeInMinutes > 0) {
25
+ this.durationsHours.push(summary.avgHoldingTimeInMinutes / 60);
83
26
  }
84
27
  }
85
28
 
86
29
  getResult() {
87
30
  const count = this.durationsHours.length;
88
- if (count === 0) {
89
- return {
90
- average_duration_hours: 0,
91
- user_count: 0
92
- };
93
- }
94
-
31
+ if (count === 0) return { average_duration_hours: 0, user_count: 0 };
95
32
  const sum = this.durationsHours.reduce((a, b) => a + b, 0);
96
-
97
- return {
98
- average_duration_hours: sum / count,
99
- user_count: count
100
- };
101
- }
102
-
103
- reset() {
104
- this.durationsHours = [];
33
+ return { average_duration_hours: sum / count, user_count: count };
105
34
  }
35
+ reset() { this.durationsHours = []; }
106
36
  }
107
-
108
37
  module.exports = AverageHoldingDurationOverall;
@@ -1,9 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Calculation (Pass 1) for P&L.
3
- *
4
- * This metric provides the *overall* platform-wide profitability ratio
5
- * (total profitable positions / total unprofitable positions)
6
- * across all users and all open positions.
3
+ * REFACTORED: Standardized extraction.
7
4
  */
8
5
  class ProfitabilityRatioOverall {
9
6
  constructor() {
@@ -11,10 +8,6 @@ class ProfitabilityRatioOverall {
11
8
  this.unprofitable = 0;
12
9
  }
13
10
 
14
- // --- NEW ---
15
- /**
16
- * Statically defines all metadata for the manifest builder.
17
- */
18
11
  static getMetadata() {
19
12
  return {
20
13
  type: 'standard',
@@ -25,50 +18,28 @@ class ProfitabilityRatioOverall {
25
18
  };
26
19
  }
27
20
 
28
- // --- NEW ---
29
- /**
30
- * Statically declare dependencies.
31
- */
32
- static getDependencies() {
33
- return [];
34
- }
21
+ static getDependencies() { return []; }
35
22
 
36
- /**
37
- * Defines the output schema for this calculation.
38
- * @returns {object} JSON Schema object
39
- */
40
23
  static getSchema() {
41
24
  return {
42
25
  "type": "object",
43
- "description": "Calculates the overall profitability ratio (profitable/unprofitable positions) for the entire platform.",
44
26
  "properties": {
45
- "overall_ratio": {
46
- "type": ["number", "null"],
47
- "description": "Ratio of profitable to unprofitable positions (profitable / unprofitable). Null if 0 unprofitable."
48
- },
49
- "total_profitable": {
50
- "type": "number",
51
- "description": "Total count of all profitable positions."
52
- },
53
- "total_unprofitable": {
54
- "type": "number",
55
- "description": "Total count of all unprofitable positions."
56
- }
27
+ "overall_ratio": { "type": ["number", "null"] },
28
+ "total_profitable": { "type": "number" },
29
+ "total_unprofitable": { "type": "number" }
57
30
  },
58
31
  "required": ["overall_ratio", "total_profitable", "total_unprofitable"]
59
32
  };
60
33
  }
61
34
 
62
- // --- REFACTORED ---
63
- // Simplified signature
64
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
65
- const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
66
- if (!positions || !Array.isArray(positions)) {
67
- return;
68
- }
35
+ process(context) {
36
+ const { extract } = context.math;
37
+ const { user } = context;
38
+
39
+ const positions = extract.getPositions(user.portfolio.today, user.type);
69
40
 
70
41
  for (const pos of positions) {
71
- const pnl = pos.NetProfit;
42
+ const pnl = extract.getNetProfit(pos); // This is a %
72
43
 
73
44
  if (pnl > 0) {
74
45
  this.profitable++;
@@ -1,156 +1,73 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for daily buy/sell sentiment.
3
- *
4
- * REFACTOR: This is now a 'type: "meta"' calculation. It runs ONCE.
5
- * It reads the pre-aggregated 'insights' data source and sums the
6
- * 'buy' and 'sell' counts from all instruments.
2
+ * @fileoverview Calculation (Pass 1) for platform-wide Buy/Sell ratio.
3
+ * REFACTORED: Uses exposure weights.
7
4
  */
8
- class DailyBuySellSentimentCount {
9
-
10
- // --- STANDARD 2: ADDED ---
5
+ class PlatformBuySellSentiment {
11
6
  constructor() {
12
- this.result = {};
7
+ this.longWeight = 0;
8
+ this.shortWeight = 0;
9
+ this.longCount = 0;
10
+ this.shortCount = 0;
13
11
  }
14
12
 
15
- /**
16
- * Statically defines all metadata for the manifest builder.
17
- */
18
13
  static getMetadata() {
19
14
  return {
20
- type: 'meta',
21
- rootDataDependencies: ['insights'], // Needs insights doc
15
+ type: 'standard',
16
+ rootDataDependencies: ['portfolio'],
22
17
  isHistorical: false,
23
- userType: 'n/a',
18
+ userType: 'all',
24
19
  category: 'core_sentiment'
25
20
  };
26
21
  }
27
22
 
28
- /**
29
- * Statically declare dependencies.
30
- */
31
- static getDependencies() {
32
- return [];
33
- }
23
+ static getDependencies() { return []; }
34
24
 
35
- /**
36
- * Defines the output schema for this calculation.
37
- */
38
25
  static getSchema() {
39
26
  return {
40
27
  "type": "object",
41
- "description": "Total count of 'buy' (long) vs 'sell' (short) positions and the sentiment ratio.",
42
28
  "properties": {
43
- "totalBuyPositions": {
44
- "type": "number",
45
- "description": "Total count of all 'buy' (long) positions."
46
- },
47
- "totalSellPositions": {
48
- "type": "number",
49
- "description": "Total count of all 'sell' (short) positions."
50
- },
51
- "sentimentRatio": {
52
- "type": ["number", "null"],
53
- "description": "Ratio of buy to sell positions (buy/sell). Null if no sell positions."
54
- }
29
+ "long_exposure_weight": { "type": "number" },
30
+ "short_exposure_weight": { "type": "number" },
31
+ "sentiment_ratio_weight": { "type": ["number", "null"] },
32
+ "sentiment_ratio_count": { "type": ["number", "null"] }
55
33
  },
56
- "required": ["totalBuyPositions", "totalSellPositions", "sentimentRatio"]
34
+ "required": ["long_exposure_weight", "short_exposure_weight", "sentiment_ratio_weight", "sentiment_ratio_count"]
57
35
  };
58
36
  }
59
37
 
60
- /**
61
- * This is a 'meta' calculation. It runs once.
62
- */
63
- // --- STANDARD 1: UPDATED SIGNATURE (added rootData, config, fetchedDependencies) ---
64
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
65
- let totalBuyPositions = 0;
66
- let totalSellPositions = 0;
67
-
68
- // ---
69
- // FIX: The test harness injects the rootData object as the *first*
70
- // argument (named 'dateStr' here) for 'meta' calcs.
71
- // 'rootData' (the 2nd arg) is used for yesterday's data and is null.
72
- // ---
73
- const rootDataToday = dateStr;
74
- const insightsDoc = rootDataToday.insights;
75
-
76
- if (!insightsDoc || !Array.isArray(insightsDoc.insights)) {
77
- dependencies.logger.log('WARN', `[daily-buy-sell-sentiment-count] No 'insights' data found.`);
78
- // --- STANDARD 2: SET STATE, DO NOT RETURN ---
79
- this.result = {
80
- totalBuyPositions: 0,
81
- totalSellPositions: 0,
82
- sentimentRatio: null
83
- };
84
- return;
85
- }
38
+ process(context) {
39
+ const { extract } = context.math;
40
+ const { user } = context;
41
+ const positions = extract.getPositions(user.portfolio.today, user.type);
86
42
 
87
- // Iterate over the pre-aggregated array
88
- for (const instrument of insightsDoc.insights) {
89
-
90
- // ---
91
- // FIX: schema.md shows 'buy' and 'sell' are percentages (e.g., 51, 49).
92
- // To get the *count*, we must use 'total' and the percentage.
93
- // total * (buy / 100)
94
- // But 'total' is the *total raw owners count*, not the total positions.
95
- // The prompt says "percentage of owners in long/short", so
96
- // 'buy' and 'sell' ARE the sentiment, not counts.
97
- //
98
- // Rereading schema.md:
99
- // "buy": 51, ---> percentage of owners in long
100
- // "sell": 49, ---> percentage of owners in short
101
- // "total": 6149, ---> total raw owners count today
102
- //
103
- // The description says "Total count of 'buy' (long) vs 'sell' (short) positions".
104
- // The old code `totalBuyPositions += instrument.buy;` assumes 'buy' is a count.
105
- // Based on the schema, 'buy' is a *percentage*.
106
- //
107
- // Let's assume the intent is to calculate the weighted average sentiment.
108
- // No, the schema *output* says "totalBuyPositions" and "totalSellPositions".
109
- //
110
- // THIS IS THE REAL BUG.
111
- // The schema 'insights' does not provide *counts* of buy/sell, only *percentages*.
112
- // The calculation *assumes* 'instrument.buy' is a count.
113
- //
114
- // Let's look at the schema again:
115
- // "buy": 51
116
- // "sell": 49
117
- // "total": 6149
118
- //
119
- // The only logical interpretation is:
120
- // Buy Count = total * (buy / 100)
121
- // Sell Count = total * (sell / 100)
122
- // ---
43
+ for (const pos of positions) {
44
+ const weight = extract.getPositionWeight(pos, user.type);
45
+ const direction = extract.getDirection(pos);
123
46
 
124
- const totalOwners = instrument.total;
125
- if (typeof totalOwners === 'number' && totalOwners > 0) {
126
- if (typeof instrument.buy === 'number') {
127
- totalBuyPositions += totalOwners * (instrument.buy / 100);
128
- }
129
- if (typeof instrument.sell === 'number') {
130
- totalSellPositions += totalOwners * (instrument.sell / 100);
131
- }
47
+ if (direction === 'Buy') {
48
+ this.longWeight += weight;
49
+ this.longCount++;
50
+ } else {
51
+ this.shortWeight += weight;
52
+ this.shortCount++;
132
53
  }
133
54
  }
134
-
135
- // --- STANDARD 2: SET STATE, DO NOT RETURN ---
136
- this.result = {
137
- // Round the counts, as they are now derived from percentages
138
- totalBuyPositions: Math.round(totalBuyPositions),
139
- totalSellPositions: Math.round(totalSellPositions),
140
- // Calculate ratio: Buy / Sell
141
- sentimentRatio: (totalSellPositions > 0) ? (totalBuyPositions / totalSellPositions) : null
142
- };
143
55
  }
144
56
 
145
- // --- STANDARD 2: ADDED ---
146
- async getResult(fetchedDependencies) {
147
- return this.result;
57
+ getResult() {
58
+ return {
59
+ long_exposure_weight: this.longWeight,
60
+ short_exposure_weight: this.shortWeight,
61
+ sentiment_ratio_weight: this.shortWeight > 0 ? this.longWeight / this.shortWeight : null,
62
+ sentiment_ratio_count: this.shortCount > 0 ? this.longCount / this.shortCount : null
63
+ };
148
64
  }
149
65
 
150
- // --- STANDARD 2: ADDED ---
151
66
  reset() {
152
- this.result = {};
67
+ this.longWeight = 0;
68
+ this.shortWeight = 0;
69
+ this.longCount = 0;
70
+ this.shortCount = 0;
153
71
  }
154
72
  }
155
-
156
- module.exports = DailyBuySellSentimentCount;
73
+ module.exports = PlatformBuySellSentiment;