aiden-shared-calculations-unified 1.0.86 → 1.0.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/calculations/core/asset-pnl-status.js +36 -106
  2. package/calculations/core/asset-position-size.js +40 -91
  3. package/calculations/core/average-daily-pnl-all-users.js +18 -57
  4. package/calculations/core/average-daily-pnl-per-sector.js +41 -88
  5. package/calculations/core/average-daily-pnl-per-stock.js +38 -91
  6. package/calculations/core/average-daily-position-pnl.js +19 -49
  7. package/calculations/core/holding-duration-per-asset.js +25 -127
  8. package/calculations/core/instrument-price-change-1d.js +30 -49
  9. package/calculations/core/instrument-price-momentum-20d.js +50 -60
  10. package/calculations/core/long-position-per-stock.js +39 -68
  11. package/calculations/core/overall-holding-duration.js +16 -87
  12. package/calculations/core/overall-profitability-ratio.js +11 -40
  13. package/calculations/core/platform-buy-sell-sentiment.js +41 -124
  14. package/calculations/core/platform-daily-bought-vs-sold-count.js +41 -99
  15. package/calculations/core/platform-daily-ownership-delta.js +68 -126
  16. package/calculations/core/platform-ownership-per-sector.js +45 -96
  17. package/calculations/core/platform-total-positions-held.js +20 -80
  18. package/calculations/core/pnl-distribution-per-stock.js +29 -135
  19. package/calculations/core/price-metrics.js +95 -206
  20. package/calculations/core/profitability-ratio-per-sector.js +34 -79
  21. package/calculations/core/profitability-ratio-per-stock.js +32 -88
  22. package/calculations/core/profitability-skew-per-stock.js +41 -94
  23. package/calculations/core/profitable-and-unprofitable-status.js +44 -76
  24. package/calculations/core/sentiment-per-stock.js +24 -77
  25. package/calculations/core/short-position-per-stock.js +35 -43
  26. package/calculations/core/social-activity-aggregation.js +26 -49
  27. package/calculations/core/social-asset-posts-trend.js +38 -94
  28. package/calculations/core/social-event-correlation.js +26 -93
  29. package/calculations/core/social-sentiment-aggregation.js +20 -44
  30. package/calculations/core/social-top-mentioned-words.js +35 -87
  31. package/calculations/core/social-topic-interest-evolution.js +22 -111
  32. package/calculations/core/social-topic-sentiment-matrix.js +38 -104
  33. package/calculations/core/social-word-mentions-trend.js +27 -104
  34. package/calculations/core/speculator-asset-sentiment.js +31 -72
  35. package/calculations/core/speculator-danger-zone.js +48 -84
  36. package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +20 -52
  37. package/calculations/core/speculator-distance-to-tp-per-leverage.js +23 -53
  38. package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +20 -50
  39. package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +23 -50
  40. package/calculations/core/speculator-leverage-per-asset.js +25 -64
  41. package/calculations/core/speculator-leverage-per-sector.js +27 -63
  42. package/calculations/core/speculator-risk-reward-ratio-per-asset.js +24 -53
  43. package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +55 -68
  44. package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +54 -71
  45. package/calculations/core/speculator-stop-loss-per-asset.js +19 -44
  46. package/calculations/core/speculator-take-profit-per-asset.js +20 -57
  47. package/calculations/core/speculator-tsl-per-asset.js +17 -56
  48. package/calculations/core/total-long-figures.js +16 -31
  49. package/calculations/core/total-long-per-sector.js +39 -61
  50. package/calculations/core/total-short-figures.js +13 -32
  51. package/calculations/core/total-short-per-sector.js +39 -61
  52. package/calculations/core/users-processed.js +11 -46
  53. package/calculations/gauss/cohort-capital-flow.js +54 -173
  54. package/calculations/gauss/cohort-definer.js +77 -163
  55. package/calculations/gauss/daily-dna-filter.js +29 -83
  56. package/calculations/gauss/gauss-divergence-signal.js +22 -109
  57. package/calculations/gem/cohort-momentum-state.js +27 -72
  58. package/calculations/gem/cohort-skill-definition.js +36 -52
  59. package/calculations/gem/platform-conviction-divergence.js +18 -60
  60. package/calculations/gem/quant-skill-alpha-signal.js +25 -98
  61. package/calculations/gem/skilled-cohort-flow.js +67 -175
  62. package/calculations/gem/skilled-unskilled-divergence.js +18 -73
  63. package/calculations/gem/unskilled-cohort-flow.js +64 -172
  64. package/calculations/helix/helix-contrarian-signal.js +20 -114
  65. package/calculations/helix/herd-consensus-score.js +42 -124
  66. package/calculations/helix/winner-loser-flow.js +36 -118
  67. package/calculations/pyro/risk-appetite-index.js +33 -74
  68. package/calculations/pyro/squeeze-potential.js +30 -87
  69. package/calculations/pyro/volatility-signal.js +33 -78
  70. package/package.json +1 -1
@@ -1,14 +1,10 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator SL distance.
3
- * --- FIX ---
4
- * - Rewritten logic to calculate SL distance from raw schema fields
5
- * (StopLossRate, CurrentRate) instead of 'PctToStopLoss'.
6
- * - Updated process signature to match worker.
2
+ * @fileoverview Calculation (Pass 1) for SL distance by sector.
3
+ * REFACTORED: Uses context.math.extract.
7
4
  */
8
-
9
- class SpeculatorSLDistanceBySectorShortLong {
5
+ class SpeculatorSLDistanceSectorBreakdown {
10
6
  constructor() {
11
- this.sectors = new Map();
7
+ this.sectorData = new Map();
12
8
  this.sectorMap = null;
13
9
  }
14
10
 
@@ -17,93 +13,85 @@ class SpeculatorSLDistanceBySectorShortLong {
17
13
  type: 'standard',
18
14
  rootDataDependencies: ['portfolio'],
19
15
  isHistorical: false,
20
- userType: 'speculator', // <-- KEY: Only runs for speculators
16
+ userType: 'speculator',
21
17
  category: 'core_speculator'
22
18
  };
23
19
  }
24
20
 
25
- static getDependencies() {
26
- return [];
27
- }
21
+ static getDependencies() { return []; }
28
22
 
29
23
  static getSchema() {
30
24
  const sectorSchema = {
31
25
  "type": "object",
32
26
  "properties": {
33
- "long_avg_distance_pct": { "type": "number" },
34
- "long_position_count": { "type": "number" },
35
- "short_avg_distance_pct": { "type": "number" },
36
- "short_position_count": { "type": "number" }
37
- }
38
- };
39
-
40
- return {
41
- "type": "object",
42
- "description": "Calculates the average distance to SL, broken down by long/short, per sector.",
43
- "patternProperties": { "^.*$": sectorSchema },
44
- "additionalProperties": sectorSchema
27
+ "avg_long_sl_distance_pct": { "type": ["number", "null"] },
28
+ "avg_short_sl_distance_pct": { "type": ["number", "null"] },
29
+ "long_count": { "type": "number" },
30
+ "short_count": { "type": "number" }
31
+ },
32
+ "required": ["avg_long_sl_distance_pct", "avg_short_sl_distance_pct", "long_count", "short_count"]
45
33
  };
34
+ return { "type": "object", "patternProperties": { "^.*$": sectorSchema } };
46
35
  }
47
36
 
48
- _initSector(sectorName) {
49
- if (!this.sectors.has(sectorName)) {
50
- this.sectors.set(sectorName, { long_sum: 0, long_count: 0, short_sum: 0, short_count: 0 });
37
+ _initSector(sector) {
38
+ if (!this.sectorData.has(sector)) {
39
+ this.sectorData.set(sector, {
40
+ longSum: 0, longCount: 0,
41
+ shortSum: 0, shortCount: 0
42
+ });
51
43
  }
52
44
  }
53
45
 
54
- // --- THIS IS THE FIX ---
55
- process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
56
- if (!this.sectorMap) {
57
- this.sectorMap = context.sectorMapping;
58
- }
46
+ process(context) {
47
+ const { extract } = context.math;
48
+ const { mappings, user } = context;
49
+ if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
59
50
 
60
- const positions = todayPortfolio.PublicPositions;
61
- if (!positions || !Array.isArray(positions) || !this.sectorMap) {
62
- return;
63
- }
51
+ const positions = extract.getPositions(user.portfolio.today, user.type);
64
52
 
65
53
  for (const pos of positions) {
66
- const instrumentId = pos.InstrumentID;
67
- const slRate = pos.StopLossRate;
68
- const currentRate = pos.CurrentRate;
54
+ const instId = extract.getInstrumentId(pos);
55
+ if (!instId) continue;
69
56
 
70
- if (!instrumentId || !slRate || slRate <= 0 || !currentRate || currentRate <= 0) {
71
- continue; // No SL or invalid data
72
- }
73
-
74
- const sectorName = this.sectorMap[instrumentId] || 'N/A';
75
- this._initSector(sectorName);
76
- const sectorData = this.sectors.get(sectorName);
57
+ const slRate = extract.getStopLossRate(pos);
58
+ const currentRate = extract.getCurrentRate(pos);
59
+ if (slRate <= 0 || currentRate <= 0) continue;
77
60
 
61
+ const sector = this.sectorMap[instId] || 'Unknown';
62
+ const direction = extract.getDirection(pos);
78
63
  let pctToSL = 0;
79
- if (pos.IsBuy) {
80
- // Long: (Current - SL) / Current
64
+
65
+ if (direction === 'Buy') {
81
66
  pctToSL = (currentRate - slRate) / currentRate;
82
- if (pctToSL > 0) {
83
- sectorData.long_sum += (pctToSL * 100);
84
- sectorData.long_count++;
85
- }
86
67
  } else {
87
- // Short: (SL - Current) / Current
88
68
  pctToSL = (slRate - currentRate) / currentRate;
89
- if (pctToSL > 0) {
90
- sectorData.short_sum += (pctToSL * 100);
91
- sectorData.short_count++;
92
- }
69
+ }
70
+
71
+ if (pctToSL <= 0) continue;
72
+
73
+ this._initSector(sector);
74
+ const data = this.sectorData.get(sector);
75
+
76
+ if (direction === 'Buy') {
77
+ data.longSum += (pctToSL * 100);
78
+ data.longCount++;
79
+ } else {
80
+ data.shortSum += (pctToSL * 100);
81
+ data.shortCount++;
93
82
  }
94
83
  }
95
84
  }
96
- // --- END FIX ---
97
85
 
98
86
  async getResult() {
99
87
  const result = {};
100
- for (const [sectorName, data] of this.sectors.entries()) {
101
- if (data.long_count > 0 || data.short_count > 0) {
102
- result[sectorName] = {
103
- long_avg_distance_pct: (data.long_count > 0) ? (data.long_sum / data.long_count) : 0,
104
- long_position_count: data.long_count,
105
- short_avg_distance_pct: (data.short_count > 0) ? (data.short_sum / data.short_count) : 0,
106
- short_position_count: data.short_count
88
+ for (const [sector, data] of this.sectorData.entries()) {
89
+ if (data.longCount > 0 || data.shortCount > 0) {
90
+ result[sector] = {
91
+ avg_long_sl_distance_pct: data.longCount > 0 ? data.longSum / data.longCount : null,
92
+ avg_short_sl_distance_pct: data.shortCount > 0 ? data.shortSum / data.shortCount : null,
93
+ long_count: data.longCount,
94
+ short_count: data.shortCount
107
95
  };
108
96
  }
109
97
  }
@@ -111,9 +99,8 @@ class SpeculatorSLDistanceBySectorShortLong {
111
99
  }
112
100
 
113
101
  reset() {
114
- this.sectors.clear();
102
+ this.sectorData.clear();
115
103
  this.sectorMap = null;
116
104
  }
117
105
  }
118
-
119
- module.exports = SpeculatorSLDistanceBySectorShortLong;
106
+ module.exports = SpeculatorSLDistanceSectorBreakdown;
@@ -1,12 +1,8 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator SL distance.
3
- * --- FIX ---
4
- * - Rewritten logic to calculate SL distance from raw schema fields
5
- * (StopLossRate, CurrentRate) instead of 'PctToStopLoss'.
6
- * - Updated process signature to match worker.
2
+ * @fileoverview Calculation (Pass 1) for SL distance broken down by direction.
3
+ * REFACTORED: Uses context.math.extract.
7
4
  */
8
-
9
- class SpeculatorSLDistanceByTickerShortLong {
5
+ class SpeculatorSLDistanceTickerBreakdown {
10
6
  constructor() {
11
7
  this.assets = new Map();
12
8
  this.tickerMap = null;
@@ -17,98 +13,86 @@ class SpeculatorSLDistanceByTickerShortLong {
17
13
  type: 'standard',
18
14
  rootDataDependencies: ['portfolio'],
19
15
  isHistorical: false,
20
- userType: 'speculator', // <-- KEY: Only runs for speculators
16
+ userType: 'speculator',
21
17
  category: 'core_speculator'
22
18
  };
23
19
  }
24
20
 
25
- static getDependencies() {
26
- return [];
27
- }
21
+ static getDependencies() { return []; }
28
22
 
29
23
  static getSchema() {
30
24
  const tickerSchema = {
31
25
  "type": "object",
32
26
  "properties": {
33
- "long_avg_distance_pct": { "type": "number" },
34
- "long_position_count": { "type": "number" },
35
- "short_avg_distance_pct": { "type": "number" },
36
- "short_position_count": { "type": "number" }
37
- }
38
- };
39
-
40
- return {
41
- "type": "object",
42
- "description": "Calculates the average distance to SL, broken down by long/short, per asset.",
43
- "patternProperties": { "^.*$": tickerSchema },
44
- "additionalProperties": tickerSchema
27
+ "avg_long_sl_distance_pct": { "type": ["number", "null"] },
28
+ "avg_short_sl_distance_pct": { "type": ["number", "null"] },
29
+ "long_count": { "type": "number" },
30
+ "short_count": { "type": "number" }
31
+ },
32
+ "required": ["avg_long_sl_distance_pct", "avg_short_sl_distance_pct", "long_count", "short_count"]
45
33
  };
34
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
46
35
  }
47
36
 
48
- _initAsset(instrumentId) {
49
- if (!this.assets.has(instrumentId)) {
50
- this.assets.set(instrumentId, { long_sum: 0, long_count: 0, short_sum: 0, short_count: 0 });
37
+ _initAsset(instId) {
38
+ if (!this.assets.has(instId)) {
39
+ this.assets.set(instId, {
40
+ longSum: 0, longCount: 0,
41
+ shortSum: 0, shortCount: 0
42
+ });
51
43
  }
52
44
  }
53
45
 
54
- // --- THIS IS THE FIX ---
55
- process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
56
- if (!this.tickerMap) {
57
- this.tickerMap = context.instrumentToTicker;
58
- }
46
+ process(context) {
47
+ const { extract } = context.math;
48
+ const { mappings, user } = context;
49
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
59
50
 
60
- const positions = todayPortfolio.PublicPositions;
61
- if (!positions || !Array.isArray(positions)) {
62
- return;
63
- }
51
+ const positions = extract.getPositions(user.portfolio.today, user.type);
64
52
 
65
53
  for (const pos of positions) {
66
- const instrumentId = pos.InstrumentID;
67
- const slRate = pos.StopLossRate;
68
- const currentRate = pos.CurrentRate;
69
-
70
- if (!instrumentId || !slRate || slRate <= 0 || !currentRate || currentRate <= 0) {
71
- continue; // No SL or invalid data
72
- }
73
-
74
- this._initAsset(instrumentId);
75
- const assetData = this.assets.get(instrumentId);
54
+ const instId = extract.getInstrumentId(pos);
55
+ const slRate = extract.getStopLossRate(pos);
56
+ const currentRate = extract.getCurrentRate(pos);
57
+
58
+ if (!instId || slRate <= 0 || currentRate <= 0) continue;
76
59
 
60
+ const direction = extract.getDirection(pos);
77
61
  let pctToSL = 0;
78
- if (pos.IsBuy) {
79
- // Long: (Current - SL) / Current
62
+
63
+ if (direction === 'Buy') {
80
64
  pctToSL = (currentRate - slRate) / currentRate;
81
- if (pctToSL > 0) {
82
- assetData.long_sum += (pctToSL * 100);
83
- assetData.long_count++;
84
- }
85
65
  } else {
86
- // Short: (SL - Current) / Current
87
66
  pctToSL = (slRate - currentRate) / currentRate;
88
- if (pctToSL > 0) {
89
- assetData.short_sum += (pctToSL * 100);
90
- assetData.short_count++;
91
- }
67
+ }
68
+
69
+ if (pctToSL <= 0) continue;
70
+
71
+ this._initAsset(instId);
72
+ const data = this.assets.get(instId);
73
+
74
+ if (direction === 'Buy') {
75
+ data.longSum += (pctToSL * 100);
76
+ data.longCount++;
77
+ } else {
78
+ data.shortSum += (pctToSL * 100);
79
+ data.shortCount++;
92
80
  }
93
81
  }
94
82
  }
95
- // --- END FIX ---
96
83
 
97
84
  async getResult() {
98
- if (!this.tickerMap) {
99
- return {};
100
- }
101
-
85
+ if (!this.tickerMap) return {};
102
86
  const result = {};
103
- for (const [instrumentId, data] of this.assets.entries()) {
104
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
105
-
106
- if (data.long_count > 0 || data.short_count > 0) {
87
+ for (const [instId, data] of this.assets.entries()) {
88
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
89
+
90
+ if (data.longCount > 0 || data.shortCount > 0) {
107
91
  result[ticker] = {
108
- long_avg_distance_pct: (data.long_count > 0) ? (data.long_sum / data.long_count) : 0,
109
- long_position_count: data.long_count,
110
- short_avg_distance_pct: (data.short_count > 0) ? (data.short_sum / data.short_count) : 0,
111
- short_position_count: data.short_count
92
+ avg_long_sl_distance_pct: data.longCount > 0 ? data.longSum / data.longCount : null,
93
+ avg_short_sl_distance_pct: data.shortCount > 0 ? data.shortSum / data.shortCount : null,
94
+ long_count: data.longCount,
95
+ short_count: data.shortCount
112
96
  };
113
97
  }
114
98
  }
@@ -120,5 +104,4 @@ class SpeculatorSLDistanceByTickerShortLong {
120
104
  this.tickerMap = null;
121
105
  }
122
106
  }
123
-
124
- module.exports = SpeculatorSLDistanceByTickerShortLong;
107
+ module.exports = SpeculatorSLDistanceTickerBreakdown;
@@ -1,13 +1,7 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator SL usage.
3
- *
4
- * This metric answers: "For each asset, what percentage
5
- * of speculators are using a stop-loss?"
6
- * --- FIX ---
7
- * - Logic changed to check 'StopLossRate > 0' instead of 'PctToStopLoss >= 0'
8
- * to match schema.md.
2
+ * @fileoverview Calculation (Pass 1) for speculator Stop Loss usage.
3
+ * REFACTORED: Uses context.math.extract.
9
4
  */
10
-
11
5
  class SpeculatorStopLossPerAsset {
12
6
  constructor() {
13
7
  this.assets = new Map();
@@ -19,14 +13,12 @@ class SpeculatorStopLossPerAsset {
19
13
  type: 'standard',
20
14
  rootDataDependencies: ['portfolio'],
21
15
  isHistorical: false,
22
- userType: 'speculator', // <-- KEY: Only runs for speculators
16
+ userType: 'speculator',
23
17
  category: 'core_speculator'
24
18
  };
25
19
  }
26
20
 
27
- static getDependencies() {
28
- return [];
29
- }
21
+ static getDependencies() { return []; }
30
22
 
31
23
  static getSchema() {
32
24
  const tickerSchema = {
@@ -38,13 +30,7 @@ class SpeculatorStopLossPerAsset {
38
30
  },
39
31
  "required": ["sl_usage_pct", "sl_count", "total_count"]
40
32
  };
41
-
42
- return {
43
- "type": "object",
44
- "description": "Calculates the percentage of speculators using a stop-loss per asset.",
45
- "patternProperties": { "^.*$": tickerSchema },
46
- "additionalProperties": tickerSchema
47
- };
33
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
48
34
  }
49
35
 
50
36
  _initAsset(instrumentId) {
@@ -53,43 +39,33 @@ class SpeculatorStopLossPerAsset {
53
39
  }
54
40
  }
55
41
 
56
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
57
- if (!this.tickerMap) {
58
- this.tickerMap = context.instrumentToTicker;
59
- }
42
+ process(context) {
43
+ const { extract } = context.math;
44
+ const { mappings, user } = context;
45
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
60
46
 
61
- const positions = todayPortfolio.PublicPositions;
62
- if (!positions || !Array.isArray(positions)) {
63
- return;
64
- }
47
+ const positions = extract.getPositions(user.portfolio.today, user.type);
65
48
 
66
49
  for (const pos of positions) {
67
- const instrumentId = pos.InstrumentID;
68
- if (!instrumentId) continue;
50
+ const instId = extract.getInstrumentId(pos);
51
+ if (!instId) continue;
69
52
 
70
- this._initAsset(instrumentId);
71
- const assetData = this.assets.get(instrumentId);
53
+ this._initAsset(instId);
54
+ const assetData = this.assets.get(instId);
72
55
  assetData.total++;
73
56
 
74
- // --- THIS IS THE FIX ---
75
- // 'PctToStopLoss' is not in schema.md. 'StopLossRate' is.
76
- // A rate > 0 indicates an SL is set.
77
- if (pos.StopLossRate > 0) {
78
- // --- END FIX ---
57
+ const slRate = extract.getStopLossRate(pos);
58
+ if (slRate > 0) {
79
59
  assetData.with_sl++;
80
60
  }
81
61
  }
82
62
  }
83
63
 
84
64
  async getResult() {
85
- if (!this.tickerMap) {
86
- return {};
87
- }
88
-
65
+ if (!this.tickerMap) return {};
89
66
  const result = {};
90
- for (const [instrumentId, data] of this.assets.entries()) {
91
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
92
-
67
+ for (const [instId, data] of this.assets.entries()) {
68
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
93
69
  if (data.total > 0) {
94
70
  result[ticker] = {
95
71
  sl_usage_pct: (data.with_sl / data.total) * 100,
@@ -106,5 +82,4 @@ class SpeculatorStopLossPerAsset {
106
82
  this.tickerMap = null;
107
83
  }
108
84
  }
109
-
110
85
  module.exports = SpeculatorStopLossPerAsset;
@@ -1,42 +1,25 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator TP usage.
3
- *
4
- * This metric answers: "For each asset, what percentage
5
- * of speculators are using a take-profit?"
2
+ * @fileoverview Calculation (Pass 1) for speculator Take Profit usage.
3
+ * REFACTORED: Uses context.math.extract to check for TP rates.
6
4
  */
7
- // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
8
-
9
5
  class SpeculatorTakeProfitPerAsset {
10
6
  constructor() {
11
- // { [instrumentId]: { with_tp: 0, total: 0 } }
12
7
  this.assets = new Map();
13
- // --- STANDARD 0: RENAMED ---
14
8
  this.tickerMap = null;
15
9
  }
16
10
 
17
- /**
18
- * Statically defines all metadata for the manifest builder.
19
- */
20
11
  static getMetadata() {
21
12
  return {
22
13
  type: 'standard',
23
14
  rootDataDependencies: ['portfolio'],
24
15
  isHistorical: false,
25
- userType: 'speculator', // <-- KEY: Only runs for speculators
16
+ userType: 'speculator',
26
17
  category: 'core_speculator'
27
18
  };
28
19
  }
29
20
 
30
- /**
31
- * Statically declare dependencies.
32
- */
33
- static getDependencies() {
34
- return [];
35
- }
21
+ static getDependencies() { return []; }
36
22
 
37
- /**
38
- * Defines the output schema for this calculation.
39
- */
40
23
  static getSchema() {
41
24
  const tickerSchema = {
42
25
  "type": "object",
@@ -47,13 +30,7 @@ class SpeculatorTakeProfitPerAsset {
47
30
  },
48
31
  "required": ["tp_usage_pct", "tp_count", "total_count"]
49
32
  };
50
-
51
- return {
52
- "type": "object",
53
- "description": "Calculates the percentage of speculators using a take-profit per asset.",
54
- "patternProperties": { "^.*$": tickerSchema },
55
- "additionalProperties": tickerSchema
56
- };
33
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
57
34
  }
58
35
 
59
36
  _initAsset(instrumentId) {
@@ -62,46 +39,34 @@ class SpeculatorTakeProfitPerAsset {
62
39
  }
63
40
  }
64
41
 
65
- // --- STANDARD 0: UPDATED SIGNATURE ---
66
- process(todayPortfolio, yesterdayPortfolio, userId, context) {
67
- // --- STANDARD 0: ADDED ---
68
- if (!this.tickerMap) {
69
- this.tickerMap = context.instrumentToTicker;
70
- }
42
+ process(context) {
43
+ const { extract } = context.math;
44
+ const { mappings, user } = context;
45
+ if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
71
46
 
72
- const positions = todayPortfolio.PublicPositions;
73
- if (!positions || !Array.isArray(positions)) {
74
- return;
75
- }
47
+ const positions = extract.getPositions(user.portfolio.today, user.type);
76
48
 
77
49
  for (const pos of positions) {
78
- const instrumentId = pos.InstrumentID;
79
- if (!instrumentId) continue;
50
+ const instId = extract.getInstrumentId(pos);
51
+ if (!instId) continue;
80
52
 
81
- this._initAsset(instrumentId);
82
- const assetData = this.assets.get(instrumentId);
53
+ this._initAsset(instId);
54
+ const assetData = this.assets.get(instId);
83
55
  assetData.total++;
84
56
 
85
- // PctToTakeProfit is -1 if no TP is set
86
- if (pos.PctToTakeProfit >= 0) {
57
+ // Check if TakeProfitRate exists and is > 0
58
+ const tpRate = extract.getTakeProfitRate(pos);
59
+ if (tpRate > 0) {
87
60
  assetData.with_tp++;
88
61
  }
89
62
  }
90
63
  }
91
64
 
92
65
  async getResult() {
93
- // --- STANDARD 0: REMOVED forbidden data load ---
94
-
95
- // Failsafe check
96
- if (!this.tickerMap) {
97
- return {}; // process() must run first
98
- }
99
-
66
+ if (!this.tickerMap) return {};
100
67
  const result = {};
101
- for (const [instrumentId, data] of this.assets.entries()) {
102
- // --- STANDARD 0: SIMPLIFIED ---
103
- const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
104
-
68
+ for (const [instId, data] of this.assets.entries()) {
69
+ const ticker = this.tickerMap[instId] || `id_${instId}`;
105
70
  if (data.total > 0) {
106
71
  result[ticker] = {
107
72
  tp_usage_pct: (data.with_tp / data.total) * 100,
@@ -115,9 +80,7 @@ class SpeculatorTakeProfitPerAsset {
115
80
 
116
81
  reset() {
117
82
  this.assets.clear();
118
- // --- STANDARD 0: RENAMED ---
119
83
  this.tickerMap = null;
120
84
  }
121
85
  }
122
-
123
86
  module.exports = SpeculatorTakeProfitPerAsset;