aiden-shared-calculations-unified 1.0.83 → 1.0.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/calculations/core/asset-pnl-status.js +122 -104
  2. package/calculations/core/asset-position-size.js +110 -73
  3. package/calculations/core/average-daily-pnl-all-users.js +17 -3
  4. package/calculations/core/average-daily-pnl-per-sector.js +83 -75
  5. package/calculations/core/average-daily-pnl-per-stock.js +84 -73
  6. package/calculations/core/average-daily-position-pnl.js +2 -2
  7. package/calculations/core/holding-duration-per-asset.js +24 -23
  8. package/calculations/core/instrument-price-change-1d.js +72 -82
  9. package/calculations/core/instrument-price-momentum-20d.js +66 -100
  10. package/calculations/core/long-position-per-stock.js +21 -13
  11. package/calculations/core/overall-holding-duration.js +8 -3
  12. package/calculations/core/overall-profitability-ratio.js +2 -2
  13. package/calculations/core/platform-buy-sell-sentiment.js +75 -22
  14. package/calculations/core/platform-daily-bought-vs-sold-count.js +19 -10
  15. package/calculations/core/platform-daily-ownership-delta.js +39 -15
  16. package/calculations/core/platform-ownership-per-sector.js +38 -18
  17. package/calculations/core/platform-total-positions-held.js +36 -14
  18. package/calculations/core/pnl-distribution-per-stock.js +39 -36
  19. package/calculations/core/price-metrics.js +70 -172
  20. package/calculations/core/profitability-ratio-per-sector.js +23 -29
  21. package/calculations/core/profitability-ratio-per-stock.js +20 -13
  22. package/calculations/core/profitability-skew-per-stock.js +20 -13
  23. package/calculations/core/profitable-and-unprofitable-status.js +34 -10
  24. package/calculations/core/sentiment-per-stock.js +20 -9
  25. package/calculations/core/short-position-per-stock.js +23 -37
  26. package/calculations/core/social-activity-aggregation.js +41 -115
  27. package/calculations/core/social-asset-posts-trend.js +77 -94
  28. package/calculations/core/social-event-correlation.js +87 -106
  29. package/calculations/core/social-sentiment-aggregation.js +56 -138
  30. package/calculations/core/social-top-mentioned-words.js +74 -106
  31. package/calculations/core/social-topic-interest-evolution.js +94 -94
  32. package/calculations/core/social-topic-sentiment-matrix.js +90 -74
  33. package/calculations/core/social-word-mentions-trend.js +92 -106
  34. package/calculations/core/speculator-asset-sentiment.js +63 -92
  35. package/calculations/core/speculator-danger-zone.js +77 -90
  36. package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +75 -90
  37. package/calculations/core/speculator-distance-to-tp-per-leverage.js +75 -88
  38. package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +75 -90
  39. package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +74 -89
  40. package/calculations/core/speculator-leverage-per-asset.js +62 -57
  41. package/calculations/core/speculator-leverage-per-sector.js +53 -65
  42. package/calculations/core/speculator-risk-reward-ratio-per-asset.js +71 -76
  43. package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +60 -81
  44. package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +57 -77
  45. package/calculations/core/speculator-stop-loss-per-asset.js +43 -80
  46. package/calculations/core/speculator-take-profit-per-asset.js +45 -69
  47. package/calculations/core/speculator-tsl-per-asset.js +42 -49
  48. package/calculations/core/total-long-figures.js +19 -19
  49. package/calculations/core/total-long-per-sector.js +39 -36
  50. package/calculations/core/total-short-figures.js +19 -19
  51. package/calculations/core/total-short-per-sector.js +39 -36
  52. package/calculations/core/users-processed.js +52 -25
  53. package/calculations/gauss/cohort-capital-flow.js +38 -29
  54. package/calculations/gauss/cohort-definer.js +17 -25
  55. package/calculations/gauss/daily-dna-filter.js +10 -4
  56. package/calculations/gauss/gauss-divergence-signal.js +28 -6
  57. package/calculations/gem/cohort-momentum-state.js +113 -92
  58. package/calculations/gem/cohort-skill-definition.js +23 -53
  59. package/calculations/gem/platform-conviction-divergence.js +62 -116
  60. package/calculations/gem/quant-skill-alpha-signal.js +107 -123
  61. package/calculations/gem/skilled-cohort-flow.js +178 -167
  62. package/calculations/gem/skilled-unskilled-divergence.js +73 -113
  63. package/calculations/gem/unskilled-cohort-flow.js +176 -166
  64. package/calculations/helix/helix-contrarian-signal.js +91 -83
  65. package/calculations/helix/herd-consensus-score.js +135 -97
  66. package/calculations/helix/winner-loser-flow.js +14 -16
  67. package/calculations/pyro/risk-appetite-index.js +121 -123
  68. package/calculations/pyro/squeeze-potential.js +93 -125
  69. package/calculations/pyro/volatility-signal.js +109 -97
  70. package/package.json +5 -5
  71. package/README.MD +0 -155
@@ -1,138 +1,123 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
3
- *
4
- * This metric answers: "For each asset and leverage level,
5
- * what is the average percentage distance from the *entry price*
6
- * to the stop loss?" (This defines the intended risk).
2
+ * @fileoverview Calculation (Pass 1) for speculator SL entry distance.
3
+ * --- FIX ---
4
+ * - Rewritten logic to calculate *entry* SL distance from raw schema fields
5
+ * (StopLossRate, OpenRate) instead of the non-existent
6
+ * 'PctToStopLossAtEntry' field.
7
+ * - Updated process signature to match worker.
7
8
  */
8
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
9
+ const BUCKETS = [1, 2, 5, 10, 20, 30]; // Leverage buckets
9
10
 
10
- class EntryDistanceToSlPerLeverage {
11
+ class SpeculatorEntryDistanceToSLPerLeverage {
11
12
  constructor() {
12
- // { [instrumentId]: { [leverage]: { sum_dist: 0, count: 0 } } }
13
- this.assets = new Map();
14
- this.mappings = null;
13
+ this.buckets = new Map();
14
+ this._initBuckets();
15
+ }
16
+
17
+ _initBuckets() {
18
+ this.buckets.clear();
19
+ for (const b of BUCKETS) {
20
+ this.buckets.set(b, { sum: 0, count: 0 });
21
+ }
22
+ this.buckets.set('other', { sum: 0, count: 0 });
15
23
  }
16
24
 
17
- // --- NEW ---
18
- /**
19
- * Statically defines all metadata for the manifest builder.
20
- */
21
25
  static getMetadata() {
22
26
  return {
23
27
  type: 'standard',
24
- rootDataDependencies: ['portfolio'], // Needs portfolio.PublicPositions
25
- isHistorical: false, // Only needs today's data
26
- userType: 'speculator', // Only runs for speculators
28
+ rootDataDependencies: ['portfolio'],
29
+ isHistorical: false,
30
+ userType: 'speculator',
27
31
  category: 'core_speculator'
28
32
  };
29
33
  }
30
34
 
31
- // --- NEW ---
32
- /**
33
- * Statically declare dependencies.
34
- */
35
35
  static getDependencies() {
36
- return []; // Pass 1
36
+ return [];
37
37
  }
38
38
 
39
- /**
40
- * Defines the output schema for this calculation.
41
- * @returns {object} JSON Schema object
42
- */
43
39
  static getSchema() {
44
- const leverageSchema = {
40
+ const bucketSchema = {
45
41
  "type": "object",
46
- "description": "Average entry-to-SL distance grouped by leverage.",
47
- "patternProperties": {
48
- // Leverage level, e.g., "1x", "5x", "10x"
49
- "^[0-9]+x$": {
50
- "type": "number",
51
- "description": "The average percentage distance from entry price to Stop Loss for this leverage level."
52
- }
53
- },
54
- "additionalProperties": { "type": "number" }
42
+ "properties": {
43
+ "avg_entry_distance_pct": { "type": "number" },
44
+ "position_count": { "type": "number" }
45
+ }
55
46
  };
56
47
 
57
48
  return {
58
49
  "type": "object",
59
- "description": "Calculates avg % distance from entry price to SL, bucketed by asset and leverage.",
60
- "patternProperties": {
61
- "^.*$": leverageSchema // Ticker
62
- },
63
- "additionalProperties": leverageSchema
50
+ "description": "Calculates the average *entry* distance to stop-loss, bucketed by leverage.",
51
+ "properties": {
52
+ "x1": bucketSchema,
53
+ "x2": bucketSchema,
54
+ "x5": bucketSchema,
55
+ "x10": bucketSchema,
56
+ "x20": bucketSchema,
57
+ "x30": bucketSchema,
58
+ "other": bucketSchema
59
+ }
64
60
  };
65
61
  }
66
62
 
67
- _initLeverage(assetData, leverage) {
68
- const key = `${leverage}x`;
69
- if (!assetData[key]) {
70
- assetData[key] = { sum_dist: 0, count: 0 };
63
+ _getBucket(leverage) {
64
+ if (this.buckets.has(leverage)) {
65
+ return this.buckets.get(leverage);
71
66
  }
72
- return key;
67
+ return this.buckets.get('other');
73
68
  }
74
69
 
75
- // --- REFACTORED ---
76
- // Simplified signature, as this calc only needs today's portfolio
77
- process(portfolioData) {
78
- if (portfolioData?.context?.userType !== 'speculator') {
79
- return;
80
- }
81
-
82
- const positions = portfolioData.PublicPositions;
70
+ // --- THIS IS THE FIX ---
71
+ process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
72
+ const positions = todayPortfolio.PublicPositions;
83
73
  if (!positions || !Array.isArray(positions)) {
84
74
  return;
85
75
  }
86
76
 
87
77
  for (const pos of positions) {
88
- const instrumentId = pos.InstrumentID;
89
- const sl_rate = pos.StopLossRate || 0;
90
- const open_rate = pos.OpenRate || 0;
91
-
92
- if (!instrumentId || sl_rate === 0 || open_rate === 0) {
93
- continue; // Skip positions without SL or open rate
78
+ const leverage = pos.Leverage;
79
+ const slRate = pos.StopLossRate;
80
+ const openRate = pos.OpenRate;
81
+
82
+ if (!leverage || !slRate || slRate <= 0 || !openRate || openRate <= 0) {
83
+ continue;
94
84
  }
95
85
 
96
- if (!this.assets.has(instrumentId)) {
97
- this.assets.set(instrumentId, {});
86
+ let pctToSL = 0;
87
+ if (pos.IsBuy) {
88
+ // Long: (Open - SL) / Open
89
+ pctToSL = (openRate - slRate) / openRate;
90
+ } else {
91
+ // Short: (SL - Open) / Open
92
+ pctToSL = (slRate - openRate) / openRate;
98
93
  }
99
- const assetData = this.assets.get(instrumentId);
100
-
101
- const leverage = pos.Leverage || 1;
102
- const key = this._initLeverage(assetData, leverage);
103
-
104
- const distance = Math.abs(open_rate - sl_rate);
105
- const distance_pct = (distance / open_rate);
106
-
107
- assetData[key].sum_dist += distance_pct;
108
- assetData[key].count++;
109
- }
110
- }
111
94
 
112
- async getResult() {
113
- if (!this.mappings) {
114
- this.mappings = await loadInstrumentMappings();
95
+ if (pctToSL <= 0) {
96
+ continue; // SL is at or beyond open price, invalid
97
+ }
98
+
99
+ const bucket = this._getBucket(leverage);
100
+ bucket.sum += (pctToSL * 100); // Convert to percentage
101
+ bucket.count++;
115
102
  }
103
+ }
104
+ // --- END FIX ---
116
105
 
106
+ getResult() {
117
107
  const result = {};
118
- for (const [instrumentId, leverageData] of this.assets.entries()) {
119
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
120
- result[ticker] = {};
121
-
122
- for (const leverageKey in leverageData) {
123
- const data = leverageData[leverageKey];
124
- if (data.count > 0) {
125
- result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
126
- }
127
- }
108
+ for (const [key, data] of this.buckets.entries()) {
109
+ const label = (key === 'other') ? 'other' : `x${key}`;
110
+ result[label] = {
111
+ avg_entry_distance_pct: (data.count > 0) ? (data.sum / data.count) : 0,
112
+ position_count: data.count
113
+ };
128
114
  }
129
115
  return result;
130
116
  }
131
117
 
132
118
  reset() {
133
- this.assets.clear();
134
- this.mappings = null;
119
+ this._initBuckets();
135
120
  }
136
121
  }
137
122
 
138
- module.exports = EntryDistanceToSlPerLeverage;
123
+ module.exports = SpeculatorEntryDistanceToSLPerLeverage;
@@ -1,138 +1,123 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
3
- *
4
- * This metric answers: "For each asset and leverage level,
5
- * what is the average percentage distance from the *entry price*
6
- * to the take profit?" (This defines the intended reward).
2
+ * @fileoverview Calculation (Pass 1) for speculator TP entry distance.
3
+ * --- FIX ---
4
+ * - Rewritten logic to calculate *entry* TP distance from raw schema fields
5
+ * (TakeProfitRate, OpenRate) instead of the non-existent
6
+ * 'PctToTakeProfitAtEntry' field.
7
+ * - Updated process signature to match worker.
7
8
  */
8
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
9
+ const BUCKETS = [1, 2, 5, 10, 20, 30]; // Leverage buckets
9
10
 
10
- class EntryDistanceToTpPerLeverage {
11
+ class SpeculatorEntryDistanceToTPPerLeverage {
11
12
  constructor() {
12
- // { [instrumentId]: { [leverage]: { sum_dist: 0, count: 0 } } }
13
- this.assets = new Map();
14
- this.mappings = null;
13
+ this.buckets = new Map();
14
+ this._initBuckets();
15
+ }
16
+
17
+ _initBuckets() {
18
+ this.buckets.clear();
19
+ for (const b of BUCKETS) {
20
+ this.buckets.set(b, { sum: 0, count: 0 });
21
+ }
22
+ this.buckets.set('other', { sum: 0, count: 0 });
15
23
  }
16
24
 
17
- // --- NEW ---
18
- /**
19
- * Statically defines all metadata for the manifest builder.
20
- */
21
25
  static getMetadata() {
22
26
  return {
23
27
  type: 'standard',
24
- rootDataDependencies: ['portfolio'], // Needs portfolio.PublicPositions
28
+ rootDataDependencies: ['portfolio'],
25
29
  isHistorical: false,
26
- userType: 'speculator', // Only runs for speculators
30
+ userType: 'speculator',
27
31
  category: 'core_speculator'
28
32
  };
29
33
  }
30
34
 
31
- // --- NEW ---
32
- /**
33
- * Statically declare dependencies.
34
- */
35
35
  static getDependencies() {
36
- return []; // Pass 1
36
+ return [];
37
37
  }
38
38
 
39
- /**
40
- * Defines the output schema for this calculation.
41
- * @returns {object} JSON Schema object
42
- */
43
39
  static getSchema() {
44
- const leverageSchema = {
40
+ const bucketSchema = {
45
41
  "type": "object",
46
- "description": "Average entry-to-TP distance grouped by leverage.",
47
- "patternProperties": {
48
- // Leverage level, e.g., "1x", "5x", "10x"
49
- "^[0-9]+x$": {
50
- "type": "number",
51
- "description": "The average percentage distance from entry price to Take Profit for this leverage level."
52
- }
53
- },
54
- "additionalProperties": { "type": "number" }
42
+ "properties": {
43
+ "avg_entry_distance_pct": { "type": "number" },
44
+ "position_count": { "type": "number" }
45
+ }
55
46
  };
56
47
 
57
48
  return {
58
49
  "type": "object",
59
- "description": "Calculates avg % distance from entry price to TP, bucketed by asset and leverage.",
60
- "patternProperties": {
61
- "^.*$": leverageSchema // Ticker
62
- },
63
- "additionalProperties": leverageSchema
50
+ "description": "Calculates the average *entry* distance to take-profit, bucketed by leverage.",
51
+ "properties": {
52
+ "x1": bucketSchema,
53
+ "x2": bucketSchema,
54
+ "x5": bucketSchema,
55
+ "x10": bucketSchema,
56
+ "x20": bucketSchema,
57
+ "x30": bucketSchema,
58
+ "other": bucketSchema
59
+ }
64
60
  };
65
61
  }
66
62
 
67
- _initLeverage(assetData, leverage) {
68
- const key = `${leverage}x`;
69
- if (!assetData[key]) {
70
- assetData[key] = { sum_dist: 0, count: 0 };
63
+ _getBucket(leverage) {
64
+ if (this.buckets.has(leverage)) {
65
+ return this.buckets.get(leverage);
71
66
  }
72
- return key;
67
+ return this.buckets.get('other');
73
68
  }
74
69
 
75
- // --- REFACTORED ---
76
- // Simplified signature
77
- process(portfolioData) {
78
- if (portfolioData?.context?.userType !== 'speculator') {
79
- return;
80
- }
81
-
82
- const positions = portfolioData.PublicPositions;
70
+ // --- THIS IS THE FIX ---
71
+ process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
72
+ const positions = todayPortfolio.PublicPositions;
83
73
  if (!positions || !Array.isArray(positions)) {
84
74
  return;
85
75
  }
86
76
 
87
77
  for (const pos of positions) {
88
- const instrumentId = pos.InstrumentID;
89
- const tp_rate = pos.TakeProfitRate || 0;
90
- const open_rate = pos.OpenRate || 0;
91
-
92
- if (!instrumentId || tp_rate === 0 || open_rate === 0) {
93
- continue; // Skip positions without TP or open rate
78
+ const leverage = pos.Leverage;
79
+ const tpRate = pos.TakeProfitRate;
80
+ const openRate = pos.OpenRate;
81
+
82
+ if (!leverage || !tpRate || tpRate <= 0 || !openRate || openRate <= 0) {
83
+ continue;
94
84
  }
95
85
 
96
- if (!this.assets.has(instrumentId)) {
97
- this.assets.set(instrumentId, {});
86
+ let pctToTP = 0;
87
+ if (pos.IsBuy) {
88
+ // Long: (TP - Open) / Open
89
+ pctToTP = (tpRate - openRate) / openRate;
90
+ } else {
91
+ // Short: (Open - TP) / Open
92
+ pctToTP = (openRate - tpRate) / openRate;
98
93
  }
99
- const assetData = this.assets.get(instrumentId);
100
-
101
- const leverage = pos.Leverage || 1;
102
- const key = this._initLeverage(assetData, leverage);
103
-
104
- const distance = Math.abs(tp_rate - open_rate);
105
- const distance_pct = (distance / open_rate);
106
-
107
- assetData[key].sum_dist += distance_pct;
108
- assetData[key].count++;
109
- }
110
- }
111
94
 
112
- async getResult() {
113
- if (!this.mappings) {
114
- this.mappings = await loadInstrumentMappings();
95
+ if (pctToTP <= 0) {
96
+ continue; // TP is at or below open price, invalid
97
+ }
98
+
99
+ const bucket = this._getBucket(leverage);
100
+ bucket.sum += (pctToTP * 100); // Convert to percentage
101
+ bucket.count++;
115
102
  }
103
+ }
104
+ // --- END FIX ---
116
105
 
106
+ getResult() {
117
107
  const result = {};
118
- for (const [instrumentId, leverageData] of this.assets.entries()) {
119
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
120
- result[ticker] = {};
121
-
122
- for (const leverageKey in leverageData) {
123
- const data = leverageData[leverageKey];
124
- if (data.count > 0) {
125
- result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
126
- }
127
- }
108
+ for (const [key, data] of this.buckets.entries()) {
109
+ const label = (key === 'other') ? 'other' : `x${key}`;
110
+ result[label] = {
111
+ avg_entry_distance_pct: (data.count > 0) ? (data.sum / data.count) : 0,
112
+ position_count: data.count
113
+ };
128
114
  }
129
115
  return result;
130
116
  }
131
117
 
132
118
  reset() {
133
- this.assets.clear();
134
- this.mappings = null;
119
+ this._initBuckets();
135
120
  }
136
121
  }
137
122
 
138
- module.exports = EntryDistanceToTpPerLeverage;
123
+ module.exports = SpeculatorEntryDistanceToTPPerLeverage;
@@ -1,44 +1,17 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
2
+ * @fileoverview Calculation (Pass 1) for speculator leverage.
3
3
  *
4
- * This metric answers: "For each asset, what is the
5
- * distribution of leverage levels used by speculators?"
4
+ * This metric answers: "For each asset, what is the average
5
+ * leverage used by speculators?"
6
6
  */
7
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
7
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
8
 
9
- class LeveragePerAsset {
9
+ class SpeculatorLeveragePerAsset {
10
10
  constructor() {
11
- // { [instrumentId]: { [leverage]: count } }
11
+ // { [instrumentId]: { sum: 0, count: 0 } }
12
12
  this.assets = new Map();
13
- this.mappings = null;
14
- }
15
-
16
- /**
17
- * Defines the output schema for this calculation.
18
- * @returns {object} JSON Schema object
19
- */
20
- static getSchema() {
21
- const leverageSchema = {
22
- "type": "object",
23
- "description": "Distribution of leverage levels for this asset.",
24
- "patternProperties": {
25
- // Leverage level, e.g., "1x", "5x"
26
- "^[0-9]+x$": {
27
- "type": "number",
28
- "description": "Count of positions at this leverage."
29
- }
30
- },
31
- "additionalProperties": { "type": "number" }
32
- };
33
-
34
- return {
35
- "type": "object",
36
- "description": "Calculates the distribution of leverage levels used by speculators for each asset.",
37
- "patternProperties": {
38
- "^.*$": leverageSchema // Ticker
39
- },
40
- "additionalProperties": leverageSchema
41
- };
13
+ // --- STANDARD 0: RENAMED ---
14
+ this.tickerMap = null;
42
15
  }
43
16
 
44
17
  /**
@@ -49,7 +22,7 @@ class LeveragePerAsset {
49
22
  type: 'standard',
50
23
  rootDataDependencies: ['portfolio'],
51
24
  isHistorical: false,
52
- userType: 'speculator',
25
+ userType: 'speculator', // <-- KEY: Only runs for speculators
53
26
  category: 'core_speculator'
54
27
  };
55
28
  }
@@ -61,56 +34,88 @@ class LeveragePerAsset {
61
34
  return [];
62
35
  }
63
36
 
37
+ /**
38
+ * Defines the output schema for this calculation.
39
+ */
40
+ static getSchema() {
41
+ const tickerSchema = {
42
+ "type": "object",
43
+ "properties": {
44
+ "avg_leverage": { "type": "number" },
45
+ "position_count": { "type": "number" }
46
+ },
47
+ "required": ["avg_leverage", "position_count"]
48
+ };
49
+
50
+ return {
51
+ "type": "object",
52
+ "description": "Calculates the average leverage used by speculators for each asset.",
53
+ "patternProperties": { "^.*$": tickerSchema },
54
+ "additionalProperties": tickerSchema
55
+ };
56
+ }
57
+
64
58
  _initAsset(instrumentId) {
65
59
  if (!this.assets.has(instrumentId)) {
66
- this.assets.set(instrumentId, {});
60
+ this.assets.set(instrumentId, { sum: 0, count: 0 });
67
61
  }
68
62
  }
69
63
 
70
- process(portfolioData) {
71
- if (portfolioData?.context?.userType !== 'speculator') {
72
- return;
64
+ // --- STANDARD 0: UPDATED SIGNATURE ---
65
+ process(todayPortfolio, yesterdayPortfolio, userId, context) {
66
+ // --- STANDARD 0: ADDED ---
67
+ if (!this.tickerMap) {
68
+ this.tickerMap = context.instrumentToTicker;
73
69
  }
74
-
75
- const positions = portfolioData.PublicPositions;
70
+
71
+ const positions = todayPortfolio.PublicPositions;
76
72
  if (!positions || !Array.isArray(positions)) {
77
73
  return;
78
74
  }
79
75
 
80
76
  for (const pos of positions) {
81
77
  const instrumentId = pos.InstrumentID;
82
- if (!instrumentId) continue;
78
+ const leverage = pos.Leverage;
79
+
80
+ if (!instrumentId || !leverage || leverage <= 1) {
81
+ continue; // Only care about leveraged positions
82
+ }
83
83
 
84
84
  this._initAsset(instrumentId);
85
85
  const assetData = this.assets.get(instrumentId);
86
-
87
- const leverage = pos.Leverage || 1;
88
- const key = `${leverage}x`;
89
-
90
- if (!assetData[key]) {
91
- assetData[key] = 0;
92
- }
93
- assetData[key]++;
86
+ assetData.sum += leverage;
87
+ assetData.count++;
94
88
  }
95
89
  }
96
90
 
97
91
  async getResult() {
98
- if (!this.mappings) {
99
- this.mappings = await loadInstrumentMappings();
92
+ // --- STANDARD 0: REMOVED forbidden data load ---
93
+
94
+ // Failsafe check
95
+ if (!this.tickerMap) {
96
+ return {}; // process() must run first
100
97
  }
101
98
 
102
99
  const result = {};
103
100
  for (const [instrumentId, data] of this.assets.entries()) {
104
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
105
- result[ticker] = data;
101
+ // --- STANDARD 0: SIMPLIFIED ---
102
+ const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
103
+
104
+ if (data.count > 0) {
105
+ result[ticker] = {
106
+ avg_leverage: data.sum / data.count,
107
+ position_count: data.count
108
+ };
109
+ }
106
110
  }
107
111
  return result;
108
112
  }
109
113
 
110
114
  reset() {
111
115
  this.assets.clear();
112
- this.mappings = null;
116
+ // --- STANDARD 0: RENAMED ---
117
+ this.tickerMap = null;
113
118
  }
114
119
  }
115
120
 
116
- module.exports = LeveragePerAsset;
121
+ module.exports = SpeculatorLeveragePerAsset;