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,148 +1,92 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 2) for daily bought vs. sold count.
3
- *
4
- * This metric tracks the total number of positions 'bought' (new)
5
- * and 'sold' (closed) today, based on daily ownership change.
6
- *
7
- * This is different from 'daily_asset_activity' because it counts
8
- * *positions*, not *unique users*.
2
+ * @fileoverview Calculation (Pass 1) for daily bought vs. sold count.
3
+ * REFACTORED: Uses context.math.extract on today vs yesterday.
9
4
  */
10
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
11
-
12
-
13
5
  class DailyBoughtVsSoldCount {
14
6
  constructor() {
15
- // We will store { [instrumentId]: { new: 0, closed: 0 } }
16
7
  this.assetActivity = new Map();
17
- // --- STANDARD 0: RENAMED ---
18
8
  this.tickerMap = null;
19
9
  }
20
10
 
21
- /**
22
- * Statically defines all metadata for the manifest builder.
23
- */
24
11
  static getMetadata() {
25
12
  return {
26
13
  type: 'standard',
27
14
  rootDataDependencies: ['portfolio'],
28
- isHistorical: true, // Needs yesterday's portfolio
15
+ isHistorical: true,
29
16
  userType: 'all',
30
17
  category: 'core_metrics'
31
18
  };
32
19
  }
33
20
 
34
- /**
35
- * Statically declare dependencies.
36
- */
37
- static getDependencies() {
38
- return [];
39
- }
21
+ static getDependencies() { return []; }
40
22
 
41
- /**
42
- * Defines the output schema for this calculation.
43
- */
44
23
  static getSchema() {
45
24
  const tickerSchema = {
46
25
  "type": "object",
47
- "description": "Daily trade activity for a specific asset.",
48
26
  "properties": {
49
- "positions_bought": {
50
- "type": "number",
51
- "description": "Total new positions opened for this asset."
52
- },
53
- "positions_sold": {
54
- "type": "number",
55
- "description": "Total positions closed for this asset."
56
- },
57
- "net_change": {
58
- "type": "number",
59
- "description": "Net change in positions (bought - sold)."
60
- }
27
+ "positions_bought": { "type": "number" },
28
+ "positions_sold": { "type": "number" },
29
+ "net_change": { "type": "number" }
61
30
  },
62
31
  "required": ["positions_bought", "positions_sold", "net_change"]
63
32
  };
64
-
65
- return {
66
- "type": "object",
67
- "description": "Tracks new positions (bought) and closed positions (sold) per asset.",
68
- "patternProperties": {
69
- "^.*$": tickerSchema // Matches any string key (ticker)
70
- },
71
- "additionalProperties": tickerSchema
72
- };
33
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
73
34
  }
74
35
 
75
36
  _initAsset(instrumentId) {
76
37
  if (!this.assetActivity.has(instrumentId)) {
77
- this.assetActivity.set(instrumentId, {
78
- new: 0,
79
- closed: 0
80
- });
38
+ this.assetActivity.set(instrumentId, { new: 0, closed: 0 });
81
39
  }
82
40
  }
83
41
 
84
- _getInstrumentIds(portfolio) {
85
- // This MUST use PositionID to be accurate
86
- const positions = portfolio?.AggregatedPositions || portfolio?.PublicPositions;
87
- if (!positions || !Array.isArray(positions)) {
88
- return new Map();
42
+ _getInstrumentIds(positions, extract) {
43
+ const map = new Map();
44
+ for (const pos of positions) {
45
+ const posId = extract.getPositionId(pos);
46
+ const instId = extract.getInstrumentId(pos);
47
+ if (posId && instId) map.set(posId, instId);
89
48
  }
90
- // Map<PositionID, InstrumentID>
91
- return new Map(positions.map(p => [p.PositionID, p.InstrumentID]).filter(p => p[0] && p[1]));
49
+ return map;
92
50
  }
93
51
 
94
- // --- STANDARD 0: UPDATED SIGNATURE (added context) ---
95
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
96
- // --- STANDARD 0: ADDED ---
97
- if (!this.tickerMap) {
98
- this.tickerMap = context.instrumentToTicker;
99
- }
52
+ process(context) {
53
+ const { extract } = context.math;
54
+ const { mappings, user } = context;
55
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
100
56
 
101
- if (!todayPortfolio || !yesterdayPortfolio) {
102
- return;
103
- }
57
+ const todayPositions = extract.getPositions(user.portfolio.today, user.type);
58
+ const yesterdayPositions = extract.getPositions(user.portfolio.yesterday, user.type);
104
59
 
105
- const yPosMap = this._getInstrumentIds(yesterdayPortfolio); // Map<PosID, InstID>
106
- const tPosMap = this._getInstrumentIds(todayPortfolio); // Map<PosID, InstID>
60
+ const tPosMap = this._getInstrumentIds(todayPositions, extract);
61
+ const yPosMap = this._getInstrumentIds(yesterdayPositions, extract);
107
62
 
108
- // Find new positions (in today but not yesterday)
109
- for (const [tPosId, tInstId] of tPosMap.entries()) {
110
- if (!yPosMap.has(tPosId)) {
111
- this._initAsset(tInstId);
112
- this.assetActivity.get(tInstId).new++;
63
+ // New positions (in today, not yesterday)
64
+ for (const [posId, instId] of tPosMap.entries()) {
65
+ if (!yPosMap.has(posId)) {
66
+ this._initAsset(instId);
67
+ this.assetActivity.get(instId).new++;
113
68
  }
114
69
  }
115
70
 
116
- // Find closed positions (in yesterday but not today)
117
- for (const [yPosId, yInstId] of yPosMap.entries()) {
118
- if (!tPosMap.has(yPosId)) {
119
- this._initAsset(yInstId);
120
- this.assetActivity.get(yInstId).closed++;
71
+ // Closed positions (in yesterday, not today)
72
+ for (const [posId, instId] of yPosMap.entries()) {
73
+ if (!tPosMap.has(posId)) {
74
+ this._initAsset(instId);
75
+ this.assetActivity.get(instId).closed++;
121
76
  }
122
77
  }
123
78
  }
124
79
 
125
80
  async getResult() {
126
- // --- STANDARD 0: REMOVED forbidden data load ---
127
-
128
- // Failsafe check
129
- if (!this.tickerMap) {
130
- return {}; // process() must run first
131
- }
132
-
81
+ if (!this.tickerMap) return {};
133
82
  const result = {};
134
- for (const [instrumentId, data] of this.assetActivity.entries()) {
135
- // --- STANDARD 0: SIMPLIFIED ---
136
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
137
-
138
- const openCount = data.new;
139
- const closeCount = data.closed;
140
-
141
- if (openCount > 0 || closeCount > 0) {
83
+ for (const [instId, data] of this.assetActivity.entries()) {
84
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
85
+ if (data.new > 0 || data.closed > 0) {
142
86
  result[ticker] = {
143
- positions_bought: openCount,
144
- positions_sold: closeCount,
145
- net_change: openCount - closeCount
87
+ positions_bought: data.new,
88
+ positions_sold: data.closed,
89
+ net_change: data.new - data.closed
146
90
  };
147
91
  }
148
92
  }
@@ -151,9 +95,7 @@ class DailyBoughtVsSoldCount {
151
95
 
152
96
  reset() {
153
97
  this.assetActivity.clear();
154
- // --- STANDARD 0: RENAMED ---
155
98
  this.tickerMap = null;
156
99
  }
157
100
  }
158
-
159
101
  module.exports = DailyBoughtVsSoldCount;
@@ -1,161 +1,103 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for daily ownership delta.
3
- *
4
- * This metric calculates the daily change in the total number of *owners*
5
- * (unique users) for each instrument.
6
- *
7
- * REFACTOR: This is now a 'type: "meta"' and 'isHistorical: true' calculation.
8
- * It runs ONCE, loads today's and yesterday's pre-aggregated 'insights' docs,
9
- * and calculates the delta based on the 'total' field.
2
+ * @fileoverview Calculation (Pass 1) for ownership delta (users added/lost per asset).
3
+ * REFACTORED: Tracks net change in number of owners. ---> TODO IN TEST ENVIRONMENT THE VALUE OF NUMBER OF CLOSURES ALWAYS RETURNS 0, IS THIS A COMPUTATION BUG OR A TEST HARNESS BUG?
10
4
  */
11
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
12
-
13
-
14
- class DailyOwnershipDelta {
15
-
16
- // --- STANDARD 2: ADDED ---
5
+ class PlatformDailyOwnershipDelta {
17
6
  constructor() {
18
- this.result = {};
7
+ this.assetChanges = new Map(); // { instId: { added: 0, removed: 0 } }
8
+ this.tickerMap = null;
19
9
  }
20
10
 
21
- /**
22
- * Statically defines all metadata for the manifest builder.
23
- */
24
11
  static getMetadata() {
25
12
  return {
26
- type: 'meta',
27
- rootDataDependencies: ['insights'], // Needs insights doc
28
- isHistorical: true, // Needs yesterday's insights doc
29
- userType: 'n/a',
13
+ type: 'standard',
14
+ rootDataDependencies: ['portfolio'],
15
+ isHistorical: true,
16
+ userType: 'all',
30
17
  category: 'core_metrics'
31
18
  };
32
19
  }
33
20
 
34
- /**
35
- * Statically declare dependencies.
36
- */
37
- static getDependencies() {
38
- return [];
39
- }
21
+ static getDependencies() { return []; }
40
22
 
41
- /**
42
- * Defines the output schema for this calculation.
43
- */
44
23
  static getSchema() {
45
24
  const tickerSchema = {
46
25
  "type": "object",
47
- "description": "Daily change in unique owners for a specific asset.",
48
26
  "properties": {
49
- "owners_today": {
50
- "type": "number",
51
- "description": "Total unique users holding this asset today."
52
- },
53
- "owners_yesterday": {
54
- "type": "number",
55
- "description": "Total unique users holding this asset yesterday."
56
- },
57
- "owner_delta": {
58
- "type": "number",
59
- "description": "The net change in unique owners (today - yesterday)."
60
- },
61
- "owner_delta_percent": {
62
- "type": ["number", "null"],
63
- "description": "Percentage change in unique owners. Null if yesterday had 0 owners."
64
- }
27
+ "owners_added": { "type": "number" },
28
+ "owners_removed": { "type": "number" },
29
+ "net_ownership_change": { "type": "number" }
65
30
  },
66
- "required": ["owners_today", "owners_yesterday", "owner_delta", "owner_delta_percent"]
31
+ "required": ["owners_added", "owners_removed", "net_ownership_change"]
67
32
  };
33
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
34
+ }
68
35
 
69
- return {
70
- "type": "object",
71
- "description": "Calculates the daily change in the total number of unique owners for each asset.",
72
- "patternProperties": {
73
- "^.*$": tickerSchema // Matches any string key (ticker)
74
- },
75
- "additionalProperties": tickerSchema
76
- };
36
+ _initAsset(instId) {
37
+ if (!this.assetChanges.has(instId)) {
38
+ this.assetChanges.set(instId, { added: 0, removed: 0 });
39
+ }
40
+ }
41
+
42
+ _getOwnedInstruments(positions, extract) {
43
+ const set = new Set();
44
+ for (const pos of positions) {
45
+ const id = extract.getInstrumentId(pos);
46
+ if (id) set.add(id);
47
+ }
48
+ return set;
77
49
  }
78
50
 
79
- /**
80
- * This is a 'meta' and 'historical' calculation. It runs once.
81
- */
82
- // --- STANDARD 1: UPDATED SIGNATURE ---
83
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
84
-
85
- // ---
86
- // FIX: The test harness ('worker.js') is now our "ground truth".
87
- //
88
- // 1. Get Ticker Mappings:
89
- // 'worker.js' passes the context object as ARGUMENT 4 (named 'config').
90
- // This context *already contains* the ticker map.
91
- // ---
92
- const { instrumentToTicker } = config; // 'config' is Param 4
93
-
94
- const todayOwners = new Map();
95
- const yesterdayOwners = new Map();
96
- const allInstrumentIds = new Set();
97
-
98
- // ---
99
- // 2. Get Data:
100
- // 'worker.js' passes the root data object as ARGUMENT 1 (named 'dateStr').
101
- // ---
102
- const rootDataToday = dateStr; // 'dateStr' is Param 1
103
-
104
- const todayDoc = rootDataToday?.insights;
105
- const yesterdayDoc = rootDataToday?.yesterdayInsights;
106
-
107
- // 1. Process today's insights doc
108
- if (todayDoc && Array.isArray(todayDoc.insights)) {
109
- for (const instrument of todayDoc.insights) {
110
- const id = instrument.instrumentId;
111
- const totalOwners = instrument.total || 0; // 'total' is the owner count
112
- todayOwners.set(id, totalOwners);
113
- allInstrumentIds.add(id);
51
+ process(context) {
52
+ const { extract } = context.math;
53
+ const { mappings, user } = context;
54
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
55
+
56
+ // If either portfolio is missing, we can't calculate delta for this user
57
+ if (!user.portfolio.today || !user.portfolio.yesterday) return;
58
+
59
+ const todayPositions = extract.getPositions(user.portfolio.today, user.type);
60
+ const yesterdayPositions = extract.getPositions(user.portfolio.yesterday, user.type);
61
+
62
+ const tSet = this._getOwnedInstruments(todayPositions, extract);
63
+ const ySet = this._getOwnedInstruments(yesterdayPositions, extract);
64
+
65
+ // Added: In Today, Not Yesterday
66
+ for (const instId of tSet) {
67
+ if (!ySet.has(instId)) {
68
+ this._initAsset(instId);
69
+ this.assetChanges.get(instId).added++;
114
70
  }
115
71
  }
116
-
117
- // 2. Process yesterday's insights doc
118
- if (yesterdayDoc && Array.isArray(yesterdayDoc.insights)) {
119
- for (const instrument of yesterdayDoc.insights) {
120
- const id = instrument.instrumentId;
121
- const totalOwners = instrument.total || 0; // 'total' is the owner count
122
- yesterdayOwners.set(id, totalOwners);
123
- allInstrumentIds.add(id);
72
+
73
+ // Removed: In Yesterday, Not Today
74
+ for (const instId of ySet) {
75
+ if (!tSet.has(instId)) {
76
+ this._initAsset(instId);
77
+ this.assetChanges.get(instId).removed++;
124
78
  }
125
79
  }
126
-
127
- // 3. Calculate deltas
80
+ }
81
+
82
+ async getResult() {
83
+ if (!this.tickerMap) return {};
128
84
  const result = {};
129
- for (const instrumentId of allInstrumentIds) {
130
- // --- Use the map from Param 4 ---
131
- const ticker = instrumentToTicker[instrumentId] || `id_${instrumentId}`;
132
-
133
- const tOwners = todayOwners.get(instrumentId) || 0;
134
- const yOwners = yesterdayOwners.get(instrumentId) || 0;
135
-
136
- if (yOwners > 0 || tOwners > 0) {
85
+ for (const [instId, data] of this.assetChanges.entries()) {
86
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
87
+ if (data.added > 0 || data.removed > 0) {
137
88
  result[ticker] = {
138
- owners_today: tOwners,
139
- owners_yesterday: yOwners,
140
- owner_delta: tOwners - yOwners,
141
- owner_delta_percent: (yOwners > 0) ? ((tOwners - yOwners) / yOwners) * 100 : null
89
+ owners_added: data.added,
90
+ owners_removed: data.removed,
91
+ net_ownership_change: data.added - data.removed
142
92
  };
143
93
  }
144
94
  }
145
-
146
- // --- STANDARD 2: SET STATE, DO NOT RETURN ---
147
- this.result = result;
95
+ return result;
148
96
  }
149
97
 
150
- // --- STANDARD 2: ADDED ---
151
- async getResult(fetchedDependencies) {
152
- return this.result;
153
- }
154
-
155
- // --- STANDARD 2: ADDED ---
156
98
  reset() {
157
- this.result = {};
99
+ this.assetChanges.clear();
100
+ this.tickerMap = null;
158
101
  }
159
102
  }
160
-
161
- module.exports = DailyOwnershipDelta;
103
+ module.exports = PlatformDailyOwnershipDelta;
@@ -1,129 +1,78 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for daily ownership per sector.
3
- *
4
- * This is a 'type: "meta"' calculation. It runs ONCE.
5
- * It reads the pre-aggregated 'insights' data source (/daily_instrument_insights/)
6
- * and uses the sector mapping provider to aggregate the total number
7
- * of owners for each sector.
2
+ * @fileoverview Calculation (Pass 1) for sector ownership.
3
+ * REFACTORED: Uses exposure weight.
8
4
  */
9
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
10
-
11
- class DailyOwnershipPerSector {
12
-
13
- // --- STANDARD 2: ADDED ---
5
+ class PlatformOwnershipPerSector {
14
6
  constructor() {
15
- this.result = {};
7
+ this.sectorData = new Map();
8
+ this.sectorMap = null;
16
9
  }
17
10
 
18
- /**
19
- * Statically defines all metadata for the manifest builder.
20
- */
21
11
  static getMetadata() {
22
12
  return {
23
- type: 'meta',
24
- rootDataDependencies: ['insights'], // Needs insights doc
13
+ type: 'standard',
14
+ rootDataDependencies: ['portfolio'],
25
15
  isHistorical: false,
26
- userType: 'n/a',
27
- category: 'core_metrics'
16
+ userType: 'all',
17
+ category: 'core'
28
18
  };
29
19
  }
30
20
 
31
- /**
32
- * Statically declare dependencies.
33
- */
34
- static getDependencies() {
35
- return [];
36
- }
21
+ static getDependencies() { return []; }
37
22
 
38
- /**
39
- * Defines the output schema for this calculation.
40
- */
41
23
  static getSchema() {
42
- const sectorSchema = {
24
+ const schema = {
43
25
  "type": "object",
44
- "description": "Aggregated ownership for a single sector.",
45
26
  "properties": {
46
- "total_owners": {
47
- "type": "number",
48
- "description": "The total number of unique owners for all assets in this sector."
49
- }
27
+ "total_exposure_weight": { "type": "number" },
28
+ "position_count": { "type": "number" }
50
29
  },
51
- "required": ["total_owners"]
52
- };
53
-
54
- return {
55
- "type": "object",
56
- "description": "Calculates the total unique owners per sector based on the 'insights' data source.",
57
- "patternProperties": {
58
- "^.*$": sectorSchema // Matches any string key (sector name)
59
- },
60
- "additionalProperties": sectorSchema
30
+ "required": ["total_exposure_weight", "position_count"]
61
31
  };
32
+ return { "type": "object", "patternProperties": { "^.*$": schema } };
62
33
  }
63
34
 
64
- /**
65
- * This is a 'meta' calculation. It runs once.
66
- */
67
- // --- STANDARD 1: UPDATED SIGNATURE ---
68
- async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
69
- // { [sectorName]: { total_owners: 0 } }
70
- const sectorOwners = new Map();
71
-
72
- // ---
73
- // FIX: The test harness ('worker.js') is our "ground truth".
74
- //
75
- // 1. Get Logger & Mappings:
76
- // 'worker.js' passes the context object as ARGUMENT 4 (named 'config').
77
- // This context *already contains* the sector map.
78
- // ---
79
- const { logger, sectorMapping } = config; // 'config' is Param 4
80
-
81
- // 2. Get the insights document
82
- // 'worker.js' passes the root data object as ARGUMENT 1 (named 'dateStr').
83
- // ---
84
- const rootDataToday = dateStr; // 'dateStr' is Param 1
85
- const insightsDoc = rootDataToday.insights;
86
-
87
- if (!insightsDoc || !Array.isArray(insightsDoc.insights)) {
88
- logger.log('WARN', `[daily-ownership-per-sector] No 'insights' data found.`);
89
- this.result = {};
90
- return;
35
+ _initSector(sector) {
36
+ if (!this.sectorData.has(sector)) {
37
+ this.sectorData.set(sector, { weight: 0, count: 0 });
91
38
  }
39
+ }
92
40
 
93
- // 3. Iterate over the pre-aggregated array
94
- for (const instrument of insightsDoc.insights) {
95
- const instrumentId = instrument.instrumentId;
96
- const totalOwners = instrument.total; // 'total' is the owner count
41
+ process(context) {
42
+ const { extract } = context.math;
43
+ const { mappings, user } = context;
44
+ if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
97
45
 
98
- if (!instrumentId || typeof totalOwners !== 'number' || totalOwners === 0) {
99
- continue;
100
- }
46
+ const positions = extract.getPositions(user.portfolio.today, user.type);
101
47
 
102
- // --- Use the map from Param 4 ---
103
- const sectorName = sectorMapping[instrumentId] || 'N/A';
48
+ for (const pos of positions) {
49
+ const instId = extract.getInstrumentId(pos);
50
+ if (!instId) continue;
104
51
 
105
- // Initialize if new
106
- if (!sectorOwners.has(sectorName)) {
107
- sectorOwners.set(sectorName, { total_owners: 0 });
108
- }
52
+ const sector = this.sectorMap[instId] || 'Unknown';
53
+ const weight = extract.getPositionWeight(pos, user.type);
109
54
 
110
- // Add this instrument's owners to the sector's total
111
- sectorOwners.get(sectorName).total_owners += totalOwners;
55
+ this._initSector(sector);
56
+ const data = this.sectorData.get(sector);
57
+ data.weight += weight;
58
+ data.count++;
112
59
  }
113
-
114
- // --- STANDARD 2: SET STATE, DO NOT RETURN ---
115
- this.result = Object.fromEntries(sectorOwners);
116
60
  }
117
61
 
118
- // --- STANDARD 2: ADDED ---
119
- async getResult(fetchedDependencies) {
120
- return this.result;
62
+ async getResult() {
63
+ const result = {};
64
+ for (const [sector, data] of this.sectorData.entries()) {
65
+ result[sector] = {
66
+ total_exposure_weight: data.weight,
67
+ position_count: data.count
68
+ };
69
+ }
70
+ return result;
121
71
  }
122
72
 
123
- // --- STANDARD 2: ADDED ---
124
73
  reset() {
125
- this.result = {};
74
+ this.sectorData.clear();
75
+ this.sectorMap = null;
126
76
  }
127
77
  }
128
-
129
- module.exports = DailyOwnershipPerSector;
78
+ module.exports = PlatformOwnershipPerSector;