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,144 +1,124 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
3
- *
4
- * This metric answers: "For each ticker, what is the
5
- * average stop loss distance (in % and value) for
6
- * long and short positions?"
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.
7
7
  */
8
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
9
8
 
10
- class StopLossDistanceByTickerShortLongBreakdown {
9
+ class SpeculatorSLDistanceByTickerShortLong {
11
10
  constructor() {
12
- // { [instrumentId]: { long_pct: [], long_val: [], short_pct: [], short_val: [] } }
13
11
  this.assets = new Map();
14
- this.mappings = null;
12
+ this.tickerMap = null;
15
13
  }
16
14
 
17
- // --- NEW ---
18
- /**
19
- * Statically defines all metadata for the manifest builder.
20
- */
21
15
  static getMetadata() {
22
16
  return {
23
17
  type: 'standard',
24
18
  rootDataDependencies: ['portfolio'],
25
19
  isHistorical: false,
26
- userType: 'speculator',
20
+ userType: 'speculator', // <-- KEY: Only runs for speculators
27
21
  category: 'core_speculator'
28
22
  };
29
23
  }
30
24
 
31
- // --- NEW ---
32
- /**
33
- * Statically declare dependencies.
34
- */
35
25
  static getDependencies() {
36
26
  return [];
37
27
  }
38
28
 
39
- /**
40
- * Defines the output schema for this calculation.
41
- * @returns {object} JSON Schema object
42
- */
43
29
  static getSchema() {
44
30
  const tickerSchema = {
45
31
  "type": "object",
46
32
  "properties": {
47
- "long_avg_dist_pct": { "type": "number" },
48
- "long_avg_dist_val": { "type": "number" },
49
- "long_count": { "type": "number" },
50
- "short_avg_dist_pct": { "type": "number" },
51
- "short_avg_dist_val": { "type": "number" },
52
- "short_count": { "type": "number" }
53
- },
54
- "required": ["long_avg_dist_pct", "long_avg_dist_val", "long_count", "short_avg_dist_pct", "short_avg_dist_val", "short_count"]
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
+ }
55
38
  };
56
39
 
57
40
  return {
58
41
  "type": "object",
59
- "description": "Calculates avg SL distance (% and value) for long/short positions, grouped by asset.",
60
- "patternProperties": {
61
- "^.*$": tickerSchema // Ticker
62
- },
42
+ "description": "Calculates the average distance to SL, broken down by long/short, per asset.",
43
+ "patternProperties": { "^.*$": tickerSchema },
63
44
  "additionalProperties": tickerSchema
64
45
  };
65
46
  }
66
47
 
67
48
  _initAsset(instrumentId) {
68
49
  if (!this.assets.has(instrumentId)) {
69
- this.assets.set(instrumentId, {
70
- long_pct: [], long_val: [], short_pct: [], short_val: []
71
- });
50
+ this.assets.set(instrumentId, { long_sum: 0, long_count: 0, short_sum: 0, short_count: 0 });
72
51
  }
73
52
  }
74
-
75
- _avg(arr) {
76
- if (arr.length === 0) return 0;
77
- return arr.reduce((a, b) => a + b, 0) / arr.length;
78
- }
79
53
 
80
- // --- REFACTORED ---
81
- // Simplified signature
82
- process(portfolioData) {
83
- if (portfolioData?.context?.userType !== 'speculator') {
84
- return;
54
+ // --- THIS IS THE FIX ---
55
+ process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
56
+ if (!this.tickerMap) {
57
+ this.tickerMap = context.instrumentToTicker;
85
58
  }
86
-
87
- const positions = portfolioData.PublicPositions;
59
+
60
+ const positions = todayPortfolio.PublicPositions;
88
61
  if (!positions || !Array.isArray(positions)) {
89
62
  return;
90
63
  }
91
64
 
92
65
  for (const pos of positions) {
93
66
  const instrumentId = pos.InstrumentID;
94
- const sl_rate = pos.StopLossRate || 0;
95
- const open_rate = pos.OpenRate || 0;
96
-
97
- if (!instrumentId || sl_rate === 0 || open_rate === 0) {
98
- continue;
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
99
72
  }
100
-
73
+
101
74
  this._initAsset(instrumentId);
102
75
  const assetData = this.assets.get(instrumentId);
103
-
104
- const distance_val = Math.abs(open_rate - sl_rate);
105
- const distance_pct = (distance_val / open_rate) * 100;
106
-
76
+
77
+ let pctToSL = 0;
107
78
  if (pos.IsBuy) {
108
- assetData.long_pct.push(distance_pct);
109
- assetData.long_val.push(distance_val);
79
+ // Long: (Current - SL) / Current
80
+ pctToSL = (currentRate - slRate) / currentRate;
81
+ if (pctToSL > 0) {
82
+ assetData.long_sum += (pctToSL * 100);
83
+ assetData.long_count++;
84
+ }
110
85
  } else {
111
- assetData.short_pct.push(distance_pct);
112
- assetData.short_val.push(distance_val);
86
+ // Short: (SL - Current) / Current
87
+ pctToSL = (slRate - currentRate) / currentRate;
88
+ if (pctToSL > 0) {
89
+ assetData.short_sum += (pctToSL * 100);
90
+ assetData.short_count++;
91
+ }
113
92
  }
114
93
  }
115
94
  }
95
+ // --- END FIX ---
116
96
 
117
97
  async getResult() {
118
- if (!this.mappings) {
119
- this.mappings = await loadInstrumentMappings();
98
+ if (!this.tickerMap) {
99
+ return {};
120
100
  }
121
101
 
122
102
  const result = {};
123
103
  for (const [instrumentId, data] of this.assets.entries()) {
124
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
125
-
126
- result[ticker] = {
127
- long_avg_dist_pct: this._avg(data.long_pct),
128
- long_avg_dist_val: this._avg(data.long_val),
129
- long_count: data.long_pct.length,
130
- short_avg_dist_pct: this._avg(data.short_pct),
131
- short_avg_dist_val: this._avg(data.short_val),
132
- short_count: data.short_pct.length
133
- };
104
+ const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
105
+
106
+ if (data.long_count > 0 || data.short_count > 0) {
107
+ 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
112
+ };
113
+ }
134
114
  }
135
115
  return result;
136
116
  }
137
117
 
138
118
  reset() {
139
119
  this.assets.clear();
140
- this.mappings = null;
120
+ this.tickerMap = null;
141
121
  }
142
122
  }
143
123
 
144
- module.exports = StopLossDistanceByTickerShortLongBreakdown;
124
+ module.exports = SpeculatorSLDistanceByTickerShortLong;
@@ -1,96 +1,64 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
2
+ * @fileoverview Calculation (Pass 1) for speculator SL usage.
3
3
  *
4
- * This metric answers: "For each asset, what is the
5
- * average stop loss level and the percentage of
6
- * positions that have a stop loss set?"
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.
7
9
  */
8
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
9
10
 
10
- class StopLossPerAsset {
11
+ class SpeculatorStopLossPerAsset {
11
12
  constructor() {
12
- // { [instrumentId]: { sl_rate_sum: 0, sl_dist_sum: 0, sl_set_count: 0, total_count: 0 } }
13
13
  this.assets = new Map();
14
- this.mappings = null;
14
+ this.tickerMap = null;
15
15
  }
16
16
 
17
- // --- NEW ---
18
- /**
19
- * Statically defines all metadata for the manifest builder.
20
- */
21
17
  static getMetadata() {
22
18
  return {
23
19
  type: 'standard',
24
20
  rootDataDependencies: ['portfolio'],
25
21
  isHistorical: false,
26
- userType: 'speculator',
22
+ userType: 'speculator', // <-- KEY: Only runs for speculators
27
23
  category: 'core_speculator'
28
24
  };
29
25
  }
30
26
 
31
- // --- NEW ---
32
- /**
33
- * Statically declare dependencies.
34
- */
35
27
  static getDependencies() {
36
28
  return [];
37
29
  }
38
30
 
39
- /**
40
- * Defines the output schema for this calculation.
41
- * @returns {object} JSON Schema object
42
- */
43
31
  static getSchema() {
44
32
  const tickerSchema = {
45
33
  "type": "object",
46
34
  "properties": {
47
- "avg_sl_rate": {
48
- "type": "number",
49
- "description": "Average SL price level (for positions with SL)."
50
- },
51
- "avg_sl_pct_dist": {
52
- "type": "number",
53
- "description": "Average SL distance from open price % (for positions with SL)."
54
- },
55
- "sl_set_count": { "type": "number" },
56
- "total_count": { "type": "number" },
57
- "sl_set_rate_pct": {
58
- "type": "number",
59
- "description": "Percentage of positions that have a SL set."
60
- }
35
+ "sl_usage_pct": { "type": "number" },
36
+ "sl_count": { "type": "number" },
37
+ "total_count": { "type": "number" }
61
38
  },
62
- "required": ["avg_sl_rate", "avg_sl_pct_dist", "sl_set_count", "total_count", "sl_set_rate_pct"]
39
+ "required": ["sl_usage_pct", "sl_count", "total_count"]
63
40
  };
64
41
 
65
42
  return {
66
43
  "type": "object",
67
- "description": "Calculates the average SL level and usage rate per asset for speculators.",
68
- "patternProperties": {
69
- "^.*$": tickerSchema // Ticker
70
- },
44
+ "description": "Calculates the percentage of speculators using a stop-loss per asset.",
45
+ "patternProperties": { "^.*$": tickerSchema },
71
46
  "additionalProperties": tickerSchema
72
47
  };
73
48
  }
74
49
 
75
50
  _initAsset(instrumentId) {
76
51
  if (!this.assets.has(instrumentId)) {
77
- this.assets.set(instrumentId, {
78
- sl_rate_sum: 0,
79
- sl_dist_sum: 0,
80
- sl_set_count: 0,
81
- total_count: 0
82
- });
52
+ this.assets.set(instrumentId, { with_sl: 0, total: 0 });
83
53
  }
84
54
  }
85
55
 
86
- // --- REFACTORED ---
87
- // Simplified signature
88
- process(portfolioData) {
89
- if (portfolioData?.context?.userType !== 'speculator') {
90
- return;
56
+ process(todayPortfolio, yesterdayPortfolio, userId, context) {
57
+ if (!this.tickerMap) {
58
+ this.tickerMap = context.instrumentToTicker;
91
59
  }
92
-
93
- const positions = portfolioData.PublicPositions;
60
+
61
+ const positions = todayPortfolio.PublicPositions;
94
62
  if (!positions || !Array.isArray(positions)) {
95
63
  return;
96
64
  }
@@ -98,50 +66,45 @@ class StopLossPerAsset {
98
66
  for (const pos of positions) {
99
67
  const instrumentId = pos.InstrumentID;
100
68
  if (!instrumentId) continue;
101
-
69
+
102
70
  this._initAsset(instrumentId);
103
71
  const assetData = this.assets.get(instrumentId);
104
- assetData.total_count++;
105
-
106
- const sl_rate = pos.StopLossRate || 0;
107
- if (sl_rate > 0) {
108
- assetData.sl_set_count++;
109
- assetData.sl_rate_sum += sl_rate;
110
-
111
- const open_rate = pos.OpenRate || 0;
112
- if (open_rate > 0) {
113
- const distance = Math.abs(open_rate - sl_rate);
114
- assetData.sl_dist_sum += (distance / open_rate);
115
- }
72
+ assetData.total++;
73
+
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 ---
79
+ assetData.with_sl++;
116
80
  }
117
81
  }
118
82
  }
119
83
 
120
84
  async getResult() {
121
- if (!this.mappings) {
122
- this.mappings = await loadInstrumentMappings();
85
+ if (!this.tickerMap) {
86
+ return {};
123
87
  }
124
88
 
125
89
  const result = {};
126
90
  for (const [instrumentId, data] of this.assets.entries()) {
127
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
128
- const count = data.sl_set_count;
129
-
130
- result[ticker] = {
131
- avg_sl_rate: (count > 0) ? (data.sl_rate_sum / count) : 0,
132
- avg_sl_pct_dist: (count > 0) ? (data.sl_dist_sum / count) * 100 : 0,
133
- sl_set_count: data.sl_set_count,
134
- total_count: data.total_count,
135
- sl_set_rate_pct: (data.total_count > 0) ? (data.sl_set_count / data.total_count) * 100 : 0
136
- };
91
+ const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
92
+
93
+ if (data.total > 0) {
94
+ result[ticker] = {
95
+ sl_usage_pct: (data.with_sl / data.total) * 100,
96
+ sl_count: data.with_sl,
97
+ total_count: data.total
98
+ };
99
+ }
137
100
  }
138
101
  return result;
139
102
  }
140
103
 
141
104
  reset() {
142
105
  this.assets.clear();
143
- this.mappings = null;
106
+ this.tickerMap = null;
144
107
  }
145
108
  }
146
109
 
147
- module.exports = StopLossPerAsset;
110
+ module.exports = SpeculatorStopLossPerAsset;
@@ -1,20 +1,19 @@
1
1
  /**
2
- * @fileoverview Calculation (Pass 1) for speculator metric.
2
+ * @fileoverview Calculation (Pass 1) for speculator TP usage.
3
3
  *
4
- * This metric answers: "For each asset, what is the
5
- * average take profit level and the percentage of
6
- * positions that have a take profit set?"
4
+ * This metric answers: "For each asset, what percentage
5
+ * of speculators are using a take-profit?"
7
6
  */
8
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
7
+ // --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
9
8
 
10
- class TakeProfitPerAsset {
9
+ class SpeculatorTakeProfitPerAsset {
11
10
  constructor() {
12
- // { [instrumentId]: { tp_rate_sum: 0, tp_dist_sum: 0, tp_set_count: 0, total_count: 0 } }
11
+ // { [instrumentId]: { with_tp: 0, total: 0 } }
13
12
  this.assets = new Map();
14
- this.mappings = null;
13
+ // --- STANDARD 0: RENAMED ---
14
+ this.tickerMap = null;
15
15
  }
16
16
 
17
- // --- NEW ---
18
17
  /**
19
18
  * Statically defines all metadata for the manifest builder.
20
19
  */
@@ -23,12 +22,11 @@ class TakeProfitPerAsset {
23
22
  type: 'standard',
24
23
  rootDataDependencies: ['portfolio'],
25
24
  isHistorical: false,
26
- userType: 'speculator',
25
+ userType: 'speculator', // <-- KEY: Only runs for speculators
27
26
  category: 'core_speculator'
28
27
  };
29
28
  }
30
29
 
31
- // --- NEW ---
32
30
  /**
33
31
  * Statically declare dependencies.
34
32
  */
@@ -38,59 +36,40 @@ class TakeProfitPerAsset {
38
36
 
39
37
  /**
40
38
  * Defines the output schema for this calculation.
41
- * @returns {object} JSON Schema object
42
39
  */
43
40
  static getSchema() {
44
41
  const tickerSchema = {
45
42
  "type": "object",
46
43
  "properties": {
47
- "avg_tp_rate": {
48
- "type": "number",
49
- "description": "Average TP price level (for positions with TP)."
50
- },
51
- "avg_tp_pct_dist": {
52
- "type": "number",
53
- "description": "Average TP distance from open price % (for positions with TP)."
54
- },
55
- "tp_set_count": { "type": "number" },
56
- "total_count": { "type": "number" },
57
- "tp_set_rate_pct": {
58
- "type": "number",
59
- "description": "Percentage of positions that have a TP set."
60
- }
44
+ "tp_usage_pct": { "type": "number" },
45
+ "tp_count": { "type": "number" },
46
+ "total_count": { "type": "number" }
61
47
  },
62
- "required": ["avg_tp_rate", "avg_tp_pct_dist", "tp_set_count", "total_count", "tp_set_rate_pct"]
48
+ "required": ["tp_usage_pct", "tp_count", "total_count"]
63
49
  };
64
50
 
65
51
  return {
66
52
  "type": "object",
67
- "description": "Calculates the average TP level and usage rate per asset for speculators.",
68
- "patternProperties": {
69
- "^.*$": tickerSchema // Ticker
70
- },
53
+ "description": "Calculates the percentage of speculators using a take-profit per asset.",
54
+ "patternProperties": { "^.*$": tickerSchema },
71
55
  "additionalProperties": tickerSchema
72
56
  };
73
57
  }
74
58
 
75
59
  _initAsset(instrumentId) {
76
60
  if (!this.assets.has(instrumentId)) {
77
- this.assets.set(instrumentId, {
78
- tp_rate_sum: 0,
79
- tp_dist_sum: 0,
80
- tp_set_count: 0,
81
- total_count: 0
82
- });
61
+ this.assets.set(instrumentId, { with_tp: 0, total: 0 });
83
62
  }
84
63
  }
85
64
 
86
- // --- REFACTORED ---
87
- // Simplified signature
88
- process(portfolioData) {
89
- if (portfolioData?.context?.userType !== 'speculator') {
90
- return;
65
+ // --- STANDARD 0: UPDATED SIGNATURE ---
66
+ process(todayPortfolio, yesterdayPortfolio, userId, context) {
67
+ // --- STANDARD 0: ADDED ---
68
+ if (!this.tickerMap) {
69
+ this.tickerMap = context.instrumentToTicker;
91
70
  }
92
-
93
- const positions = portfolioData.PublicPositions;
71
+
72
+ const positions = todayPortfolio.PublicPositions;
94
73
  if (!positions || !Array.isArray(positions)) {
95
74
  return;
96
75
  }
@@ -101,47 +80,44 @@ class TakeProfitPerAsset {
101
80
 
102
81
  this._initAsset(instrumentId);
103
82
  const assetData = this.assets.get(instrumentId);
104
- assetData.total_count++;
105
-
106
- const tp_rate = pos.TakeProfitRate || 0;
107
- if (tp_rate > 0) {
108
- assetData.tp_set_count++;
109
- assetData.tp_rate_sum += tp_rate;
110
-
111
- const open_rate = pos.OpenRate || 0;
112
- if (open_rate > 0) {
113
- const distance = Math.abs(tp_rate - open_rate);
114
- assetData.tp_dist_sum += (distance / open_rate);
115
- }
83
+ assetData.total++;
84
+
85
+ // PctToTakeProfit is -1 if no TP is set
86
+ if (pos.PctToTakeProfit >= 0) {
87
+ assetData.with_tp++;
116
88
  }
117
89
  }
118
90
  }
119
91
 
120
92
  async getResult() {
121
- if (!this.mappings) {
122
- this.mappings = await loadInstrumentMappings();
93
+ // --- STANDARD 0: REMOVED forbidden data load ---
94
+
95
+ // Failsafe check
96
+ if (!this.tickerMap) {
97
+ return {}; // process() must run first
123
98
  }
124
99
 
125
100
  const result = {};
126
101
  for (const [instrumentId, data] of this.assets.entries()) {
127
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
128
- const count = data.tp_set_count;
102
+ // --- STANDARD 0: SIMPLIFIED ---
103
+ const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
129
104
 
130
- result[ticker] = {
131
- avg_tp_rate: (count > 0) ? (data.tp_rate_sum / count) : 0,
132
- avg_tp_pct_dist: (count > 0) ? (data.tp_dist_sum / count) * 100 : 0,
133
- tp_set_count: data.tp_set_count,
134
- total_count: data.total_count,
135
- tp_set_rate_pct: (data.total_count > 0) ? (data.tp_set_count / data.total_count) * 100 : 0
136
- };
105
+ if (data.total > 0) {
106
+ result[ticker] = {
107
+ tp_usage_pct: (data.with_tp / data.total) * 100,
108
+ tp_count: data.with_tp,
109
+ total_count: data.total
110
+ };
111
+ }
137
112
  }
138
113
  return result;
139
114
  }
140
115
 
141
116
  reset() {
142
117
  this.assets.clear();
143
- this.mappings = null;
118
+ // --- STANDARD 0: RENAMED ---
119
+ this.tickerMap = null;
144
120
  }
145
121
  }
146
122
 
147
- module.exports = TakeProfitPerAsset;
123
+ module.exports = SpeculatorTakeProfitPerAsset;