aiden-shared-calculations-unified 1.0.95 → 1.0.97

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 (61) hide show
  1. package/calculations/capitulation/asset-volatility-estimator.js +1 -2
  2. package/calculations/capitulation/retail-capitulation-risk-forecast.js +1 -2
  3. package/calculations/core/Insights-total-long-per-stock +56 -0
  4. package/calculations/core/insights-daily-bought-vs-sold-count.js +74 -0
  5. package/calculations/core/insights-daily-ownership-delta.js +70 -0
  6. package/calculations/core/insights-sentimet-per-stock.js +68 -0
  7. package/calculations/core/insights-total-long-per-sector +73 -0
  8. package/calculations/core/insights-total-positions-held.js +49 -0
  9. package/calculations/ghost-book/cost-basis-density.js +1 -2
  10. package/calculations/ghost-book/liquidity-vacuum.js +4 -4
  11. package/calculations/ghost-book/retail-gamma-exposure.js +0 -1
  12. package/calculations/helix/winner-loser-flow.js +1 -1
  13. package/calculations/predicative-alpha/cognitive-dissonance.js +1 -2
  14. package/calculations/predicative-alpha/diamond-hand-fracture.js +1 -2
  15. package/calculations/predicative-alpha/mimetic-latency.js +1 -2
  16. package/package.json +1 -1
  17. package/calculations/legacy/activity_by_pnl_status.js +0 -119
  18. package/calculations/legacy/asset_crowd_flow.js +0 -163
  19. package/calculations/legacy/capital_deployment_strategy.js +0 -108
  20. package/calculations/legacy/capital_liquidation_performance.js +0 -139
  21. package/calculations/legacy/capital_vintage_performance.js +0 -136
  22. package/calculations/legacy/cash-flow-deployment.js +0 -144
  23. package/calculations/legacy/cash-flow-liquidation.js +0 -146
  24. package/calculations/legacy/crowd-cash-flow-proxy.js +0 -128
  25. package/calculations/legacy/crowd_conviction_score.js +0 -261
  26. package/calculations/legacy/crowd_sharpe_ratio_proxy.js +0 -137
  27. package/calculations/legacy/daily_asset_activity.js +0 -128
  28. package/calculations/legacy/daily_user_activity_tracker.js +0 -182
  29. package/calculations/legacy/deposit_withdrawal_percentage.js +0 -125
  30. package/calculations/legacy/diversification_pnl.js +0 -115
  31. package/calculations/legacy/drawdown_response.js +0 -137
  32. package/calculations/legacy/dumb-cohort-flow.js +0 -238
  33. package/calculations/legacy/gain_response.js +0 -137
  34. package/calculations/legacy/historical_performance_aggregator.js +0 -85
  35. package/calculations/legacy/in_loss_asset_crowd_flow.js +0 -168
  36. package/calculations/legacy/in_profit_asset_crowd_flow.js +0 -168
  37. package/calculations/legacy/negative_expectancy_cohort_flow.js +0 -232
  38. package/calculations/legacy/new_allocation_percentage.js +0 -98
  39. package/calculations/legacy/paper_vs_diamond_hands.js +0 -107
  40. package/calculations/legacy/position_count_pnl.js +0 -120
  41. package/calculations/legacy/positive_expectancy_cohort_flow.js +0 -232
  42. package/calculations/legacy/profit_cohort_divergence.js +0 -115
  43. package/calculations/legacy/profitability_migration.js +0 -104
  44. package/calculations/legacy/reallocation_increase_percentage.js +0 -104
  45. package/calculations/legacy/risk_appetite_change.js +0 -97
  46. package/calculations/legacy/sector_rotation.js +0 -117
  47. package/calculations/legacy/shark_attack_signal.js +0 -112
  48. package/calculations/legacy/smart-cohort-flow.js +0 -238
  49. package/calculations/legacy/smart-dumb-divergence-index.js +0 -143
  50. package/calculations/legacy/smart_dumb_divergence_index_v2.js +0 -138
  51. package/calculations/legacy/smart_money_flow.js +0 -198
  52. package/calculations/legacy/social-predictive-regime-state.js +0 -102
  53. package/calculations/legacy/social-topic-driver-index.js +0 -147
  54. package/calculations/legacy/social-topic-predictive-potential.js +0 -461
  55. package/calculations/legacy/social_flow_correlation.js +0 -112
  56. package/calculations/legacy/speculator_adjustment_activity.js +0 -103
  57. package/calculations/legacy/strategy-performance.js +0 -265
  58. package/calculations/legacy/tsl_effectiveness.js +0 -85
  59. package/calculations/legacy/user-investment-profile.js +0 -313
  60. package/calculations/legacy/user_expectancy_score.js +0 -106
  61. package/calculations/legacy/user_profitability_tracker.js +0 -131
@@ -13,8 +13,7 @@ class AssetVolatilityEstimator {
13
13
  type: 'meta',
14
14
  rootDataDependencies: ['price'],
15
15
  isHistorical: false,
16
- userType: 'n/a',
17
- category: 'market_stats'
16
+ userType: 'n/a'
18
17
  };
19
18
  }
20
19
 
@@ -21,8 +21,7 @@ class RetailCapitulationRiskForecast {
21
21
  type: 'standard',
22
22
  rootDataDependencies: ['portfolio', 'history'],
23
23
  isHistorical: false,
24
- userType: 'all',
25
- category: 'risk_models'
24
+ userType: 'all'
26
25
  };
27
26
  }
28
27
 
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @fileoverview Calculation (Pass 1) for total long figures.
3
+ * REFACTORED: Uses INSIGHTS DATA.
4
+ * TYPE: META
5
+ */
6
+ class InsightsTotalLongFigures {
7
+ constructor() {
8
+ this.totalPositions = 0;
9
+ }
10
+
11
+ static getMetadata() {
12
+ return {
13
+ type: 'meta',
14
+ rootDataDependencies: ['insights'],
15
+ isHistorical: false,
16
+ category: 'core_sentiment'
17
+ };
18
+ }
19
+
20
+ static getDependencies() { return []; }
21
+
22
+ static getSchema() {
23
+ return {
24
+ "type": "object",
25
+ "properties": {
26
+ "total_long_exposure_weight": { "type": "number" },
27
+ "total_positions_count": { "type": "number" }
28
+ },
29
+ "required": ["total_long_exposure_weight", "total_positions_count"]
30
+ };
31
+ }
32
+
33
+ process(context) {
34
+ const { insights: insightsHelper } = context.math;
35
+ const insights = insightsHelper.getInsights(context);
36
+
37
+ if (!insights) return;
38
+
39
+ for (const insight of insights) {
40
+ this.totalPositions += insightsHelper.getLongCount(insight);
41
+ }
42
+ }
43
+
44
+ getResult() {
45
+ return {
46
+ total_long_exposure_weight: 0, // Not available in Insights
47
+ total_positions_count: this.totalPositions
48
+ };
49
+ }
50
+
51
+ reset() {
52
+ this.totalPositions = 0;
53
+ }
54
+ }
55
+
56
+ module.exports = InsightsTotalLongFigures;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @fileoverview Calculation (Pass 1) for daily bought vs. sold count.
3
+ * REFACTORED: Uses INSIGHTS DATA as Single Source of Truth.
4
+ * TYPE: META (Runs once against global data)
5
+ */
6
+ class InsightsDailyBoughtVsSoldCount {
7
+ constructor() {
8
+ this.results = {};
9
+ }
10
+
11
+ static getMetadata() {
12
+ return {
13
+ type: 'meta',
14
+ rootDataDependencies: ['insights'], // explicit dependency on insights
15
+ isHistorical: true,
16
+ category: 'core_metrics'
17
+ };
18
+ }
19
+
20
+ static getDependencies() { return []; }
21
+
22
+ static getSchema() {
23
+ const tickerSchema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "positions_bought": { "type": "number" },
27
+ "positions_sold": { "type": "number" },
28
+ "net_change": { "type": "number" }
29
+ },
30
+ "required": ["positions_bought", "positions_sold", "net_change"]
31
+ };
32
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
33
+ }
34
+
35
+ process(context) {
36
+ const { insights: insightsHelper } = context.math; // Maps to InsightsExtractor
37
+ const insights = insightsHelper.getInsights(context);
38
+ const { mappings } = context;
39
+ const tickerMap = mappings.instrumentToTicker || {};
40
+
41
+ if (!insights || !Array.isArray(insights)) return;
42
+
43
+ for (const insight of insights) {
44
+ const instId = insight.instrumentId;
45
+ const ticker = tickerMap[instId] || `id_${instId}`;
46
+
47
+ const netChange = insightsHelper.getNetOwnershipChange(insight);
48
+
49
+ // Insights schema only provides Net Growth.
50
+ // We cannot strictly separate Bought vs Sold.
51
+ // Logic: If Net Positive -> Bought = Net, Sold = 0.
52
+ // If Net Negative -> Bought = 0, Sold = Abs(Net).
53
+ const bought = netChange > 0 ? netChange : 0;
54
+ const sold = netChange < 0 ? Math.abs(netChange) : 0;
55
+
56
+ if (bought > 0 || sold > 0) {
57
+ this.results[ticker] = {
58
+ positions_bought: bought,
59
+ positions_sold: sold,
60
+ net_change: netChange
61
+ };
62
+ }
63
+ }
64
+ }
65
+
66
+ async getResult() {
67
+ return this.results;
68
+ }
69
+
70
+ reset() {
71
+ this.results = {};
72
+ }
73
+ }
74
+ module.exports = InsightsDailyBoughtVsSoldCount;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @fileoverview Calculation (Pass 1) for ownership delta (users added/lost per asset).
3
+ * REFACTORED: Uses INSIGHTS DATA.
4
+ * TYPE: META
5
+ */
6
+ class InsightsDailyOwnershipDelta {
7
+ constructor() {
8
+ this.results = {};
9
+ }
10
+
11
+ static getMetadata() {
12
+ return {
13
+ type: 'meta',
14
+ rootDataDependencies: ['insights'],
15
+ isHistorical: true,
16
+ category: 'core_metrics'
17
+ };
18
+ }
19
+
20
+ static getDependencies() { return []; }
21
+
22
+ static getSchema() {
23
+ const tickerSchema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "owners_added": { "type": "number" },
27
+ "owners_removed": { "type": "number" },
28
+ "net_ownership_change": { "type": "number" }
29
+ },
30
+ "required": ["owners_added", "owners_removed", "net_ownership_change"]
31
+ };
32
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
33
+ }
34
+
35
+ process(context) {
36
+ const { insights: insightsHelper } = context.math;
37
+ const insights = insightsHelper.getInsights(context);
38
+ const { mappings } = context;
39
+ const tickerMap = mappings.instrumentToTicker || {};
40
+
41
+ if (!insights) return;
42
+
43
+ for (const insight of insights) {
44
+ const netChange = insightsHelper.getNetOwnershipChange(insight);
45
+ if (netChange === 0) continue;
46
+
47
+ const instId = insight.instrumentId;
48
+ const ticker = tickerMap[instId] || `id_${instId}`;
49
+
50
+ // Approximate added/removed based on Net Change logic
51
+ const added = netChange > 0 ? netChange : 0;
52
+ const removed = netChange < 0 ? Math.abs(netChange) : 0;
53
+
54
+ this.results[ticker] = {
55
+ owners_added: added,
56
+ owners_removed: removed,
57
+ net_ownership_change: netChange
58
+ };
59
+ }
60
+ }
61
+
62
+ async getResult() {
63
+ return this.results;
64
+ }
65
+
66
+ reset() {
67
+ this.results = {};
68
+ }
69
+ }
70
+ module.exports = InsightsDailyOwnershipDelta;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @fileoverview Calculation (Pass 1) for sentiment per stock.
3
+ * REFACTORED: Uses INSIGHTS DATA.
4
+ * TYPE: META
5
+ */
6
+ class InsightsSentimentPerStock {
7
+ constructor() {
8
+ this.results = {};
9
+ }
10
+
11
+ static getSchema() {
12
+ const tickerSchema = {
13
+ "type": "object",
14
+ "properties": {
15
+ "long_count": { "type": "number" },
16
+ "short_count": { "type": "number" },
17
+ "sentiment_ratio": { "type": ["number", "null"] }
18
+ },
19
+ "required": ["long_count", "short_count", "sentiment_ratio"]
20
+ };
21
+ return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
22
+ }
23
+
24
+ static getMetadata() {
25
+ return {
26
+ type: 'meta',
27
+ rootDataDependencies: ['insights'],
28
+ isHistorical: false,
29
+ category: 'core_sentiment'
30
+ };
31
+ }
32
+
33
+ static getDependencies() { return []; }
34
+
35
+ process(context) {
36
+ const { insights: insightsHelper } = context.math;
37
+ const insights = insightsHelper.getInsights(context);
38
+ const { mappings } = context;
39
+ const tickerMap = mappings.instrumentToTicker || {};
40
+
41
+ if (!insights) return;
42
+
43
+ for (const insight of insights) {
44
+ const longCount = insightsHelper.getLongCount(insight);
45
+ const shortCount = insightsHelper.getShortCount(insight);
46
+
47
+ if (longCount === 0 && shortCount === 0) continue;
48
+
49
+ const instId = insight.instrumentId;
50
+ const ticker = tickerMap[instId] || `id_${instId}`;
51
+
52
+ this.results[ticker] = {
53
+ long_count: longCount,
54
+ short_count: shortCount,
55
+ sentiment_ratio: (shortCount > 0) ? (longCount / shortCount) : null
56
+ };
57
+ }
58
+ }
59
+
60
+ async getResult() {
61
+ return this.results;
62
+ }
63
+
64
+ reset() {
65
+ this.results = {};
66
+ }
67
+ }
68
+ module.exports = InsightsSentimentPerStock;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @fileoverview Calculation (Pass 1) for long figures per sector.
3
+ * REFACTORED: Uses INSIGHTS DATA.
4
+ * TYPE: META
5
+ */
6
+ class InsightsTotalLongPerSector {
7
+ constructor() {
8
+ this.sectorData = new Map();
9
+ }
10
+
11
+ static getMetadata() {
12
+ return {
13
+ type: 'meta',
14
+ rootDataDependencies: ['insights'],
15
+ isHistorical: false,
16
+ category: 'core_sentiment'
17
+ };
18
+ }
19
+
20
+ static getDependencies() { return []; }
21
+
22
+ static getSchema() {
23
+ const schema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "total_long_exposure_weight": { "type": "number" },
27
+ "total_positions_count": { "type": "number" }
28
+ },
29
+ "required": ["total_long_exposure_weight", "total_positions_count"]
30
+ };
31
+ return { "type": "object", "patternProperties": { "^.*$": schema } };
32
+ }
33
+
34
+ process(context) {
35
+ const { insights: insightsHelper } = context.math;
36
+ const insights = insightsHelper.getInsights(context);
37
+ const { mappings } = context;
38
+ const sectorMap = mappings.instrumentToSector || {};
39
+
40
+ if (!insights) return;
41
+
42
+ for (const insight of insights) {
43
+ const longCount = insightsHelper.getLongCount(insight);
44
+ if (longCount <= 0) continue;
45
+
46
+ const sector = sectorMap[insight.instrumentId] || 'Unknown';
47
+
48
+ if (!this.sectorData.has(sector)) {
49
+ this.sectorData.set(sector, { count: 0 });
50
+ }
51
+ const data = this.sectorData.get(sector);
52
+ data.count += longCount;
53
+ }
54
+ }
55
+
56
+ async getResult() {
57
+ const result = {};
58
+ for (const [sector, data] of this.sectorData.entries()) {
59
+ if (data.count > 0) {
60
+ result[sector] = {
61
+ total_long_exposure_weight: 0, // Not available
62
+ total_positions_count: data.count
63
+ };
64
+ }
65
+ }
66
+ return result;
67
+ }
68
+
69
+ reset() {
70
+ this.sectorData.clear();
71
+ }
72
+ }
73
+ module.exports = InsightsTotalLongPerSector;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @fileoverview Calculation (Pass 1) for total positions count.
3
+ * REFACTORED: Uses INSIGHTS DATA.
4
+ * TYPE: META
5
+ */
6
+ class InsightsTotalPositionsHeld {
7
+ constructor() {
8
+ this.totalPositions = 0;
9
+ }
10
+
11
+ static getMetadata() {
12
+ return {
13
+ type: 'meta',
14
+ rootDataDependencies: ['insights'],
15
+ isHistorical: false,
16
+ category: 'Global-Platform-Metrics'
17
+ };
18
+ }
19
+
20
+ static getDependencies() { return []; }
21
+
22
+ static getSchema() {
23
+ return {
24
+ "type": "object",
25
+ "properties": {
26
+ "total_positions_count": { "type": "number" }
27
+ },
28
+ "required": ["total_positions_count"]
29
+ };
30
+ }
31
+
32
+ process(context) {
33
+ const { insights: insightsHelper } = context.math;
34
+ const insights = insightsHelper.getInsights(context);
35
+
36
+ if (!insights) return;
37
+
38
+ for (const insight of insights) {
39
+ this.totalPositions += insightsHelper.getTotalOwners(insight);
40
+ }
41
+ }
42
+
43
+ getResult() {
44
+ return { total_positions_count: this.totalPositions };
45
+ }
46
+
47
+ reset() { this.totalPositions = 0; }
48
+ }
49
+ module.exports = InsightsTotalPositionsHeld;
@@ -8,8 +8,7 @@ class CostBasisDensity {
8
8
  static getMetadata() {
9
9
  return {
10
10
  type: 'meta', // Runs ONCE per day
11
- dependencies: ['asset-cost-basis-profile'],
12
- category: 'ghost_book'
11
+ dependencies: ['asset-cost-basis-profile']
13
12
  };
14
13
  }
15
14
 
@@ -4,8 +4,7 @@ class LiquidityVacuum {
4
4
  static getMetadata() {
5
5
  return {
6
6
  type: 'meta',
7
- dependencies: ['asset-cost-basis-profile'],
8
- category: 'ghost_book'
7
+ dependencies: ['asset-cost-basis-profile']
9
8
  };
10
9
  }
11
10
 
@@ -17,13 +16,14 @@ class LiquidityVacuum {
17
16
 
18
17
  process(context) {
19
18
  const { computed, math } = context;
20
- const { distribution } = math; // Correctly accesses 'distribution' from context
19
+ const { distribution } = math;
21
20
 
22
21
  const tickers = Object.keys(computed['asset-cost-basis-profile'] || {});
23
22
 
24
23
  for (const ticker of tickers) {
25
24
  const data = computed['asset-cost-basis-profile'][ticker];
26
- if (!data) continue;
25
+ // BUG FIX: Ensure data and profile exist before accessing length/integration
26
+ if (!data || !data.profile || !Array.isArray(data.profile)) continue;
27
27
 
28
28
  const current = data.current_price;
29
29
  const totalInv = data.total_inventory_weight;
@@ -10,7 +10,6 @@ class RetailGammaExposure {
10
10
  type: 'meta',
11
11
  dependencies: ['skilled-cohort-flow', 'instrument-price-change-1d'],
12
12
  isHistorical: true, // Requires t-1, t-2... for rolling regression
13
- category: 'ghost_book'
14
13
  };
15
14
  }
16
15
 
@@ -16,7 +16,7 @@ class WinnerLoserFlow {
16
16
  userType: 'all',
17
17
  category: 'helix'
18
18
  };
19
- }
19
+ }s
20
20
 
21
21
  static getDependencies() { return []; }
22
22
 
@@ -13,8 +13,7 @@ class CognitiveDissonance {
13
13
  type: 'meta', // Runs after standard calculations
14
14
  rootDataDependencies: [],
15
15
  isHistorical: true, // Requires t-1 state
16
- userType: 'aggregate',
17
- category: 'predictive_alpha'
16
+ userType: 'aggregate'
18
17
  };
19
18
  }
20
19
 
@@ -10,8 +10,7 @@ class DiamondHandFracture {
10
10
  type: 'meta',
11
11
  rootDataDependencies: [],
12
12
  isHistorical: true,
13
- userType: 'aggregate',
14
- category: 'predictive_alpha'
13
+ userType: 'aggregate'
15
14
  };
16
15
  }
17
16
 
@@ -15,8 +15,7 @@ class MimeticLatencyOscillator {
15
15
  type: 'meta',
16
16
  rootDataDependencies: [],
17
17
  isHistorical: true,
18
- userType: 'aggregate',
19
- category: 'predictive_alpha'
18
+ userType: 'aggregate'
20
19
  };
21
20
  }
22
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiden-shared-calculations-unified",
3
- "version": "1.0.95",
3
+ "version": "1.0.97",
4
4
  "description": "Shared calculation modules for the BullTrackers Computation System.",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -1,119 +0,0 @@
1
- /**
2
- * @fileoverview Analyzes *why* users were active by checking the P&L status
3
- * of positions just before they were closed.
4
- * This measures "Profit Taking" vs. "Capitulation".
5
- */
6
- class ActivityByPnlStatus {
7
- constructor() {
8
- this.total_positions_yesterday = {
9
- in_profit: 0,
10
- in_loss: 0
11
- };
12
- this.closed_positions_today = {
13
- profit_taken: 0,
14
- loss_realized: 0
15
- };
16
- }
17
-
18
- /**
19
- * Defines the output schema for this calculation.
20
- * @returns {object} JSON Schema object
21
- */
22
- static getSchema() {
23
- return {
24
- "type": "object",
25
- "description": "Measures profit-taking vs. capitulation by analyzing the P&L of closed positions.",
26
- "properties": {
27
- "profit_taking_rate_pct": {
28
- "type": "number",
29
- "description": "The percentage of profitable positions (from yesterday) that were closed today."
30
- },
31
- "capitulation_rate_pct": {
32
- "type": "number",
33
- "description": "The percentage of losing positions (from yesterday) that were closed today."
34
- },
35
- "raw_counts": {
36
- "type": "object",
37
- "description": "Raw counts used to calculate the rates.",
38
- "properties": {
39
- "profit_positions_closed": { "type": "number" },
40
- "loss_positions_closed": { "type": "number" },
41
- "total_profit_positions_available": { "type": "number" },
42
- "total_loss_positions_available": { "type": "number" }
43
- },
44
- "required": ["profit_positions_closed", "loss_positions_closed", "total_profit_positions_available", "total_loss_positions_available"]
45
- }
46
- },
47
- "required": ["profit_taking_rate_pct", "capitulation_rate_pct", "raw_counts"]
48
- };
49
- }
50
-
51
- _getPortfolioMap(portfolio) {
52
- // We MUST use PositionID here to track specific trades, not just the asset
53
- const positions = portfolio?.AggregatedPositions || portfolio?.PublicPositions;
54
- if (!positions || !Array.isArray(positions)) {
55
- return new Map();
56
- }
57
- // Map<PositionID, NetProfit>
58
- return new Map(positions.map(p => [p.PositionID, p.NetProfit || 0]));
59
- }
60
-
61
- process(todayPortfolio, yesterdayPortfolio, userId) {
62
- if (!todayPortfolio || !yesterdayPortfolio) {
63
- return;
64
- }
65
-
66
- const yPosMap = this._getPortfolioMap(yesterdayPortfolio);
67
- const tPosMap = this._getPortfolioMap(todayPortfolio);
68
-
69
- if (yPosMap.size === 0) {
70
- return; // No positions yesterday to analyze
71
- }
72
-
73
- for (const [yPosId, yNetProfit] of yPosMap.entries()) {
74
- // 1. Bucket yesterday's P&L state
75
- if (yNetProfit > 0) {
76
- this.total_positions_yesterday.in_profit++;
77
- } else if (yNetProfit < 0) {
78
- this.total_positions_yesterday.in_loss++;
79
- }
80
-
81
- // 2. Check if this position was closed
82
- if (!tPosMap.has(yPosId)) {
83
- // Position was closed. Check its P&L from yesterday.
84
- if (yNetProfit > 0) {
85
- this.closed_positions_today.profit_taken++;
86
- } else if (yNetProfit < 0) {
87
- this.closed_positions_today.loss_realized++;
88
- }
89
- }
90
- }
91
- }
92
-
93
- getResult() {
94
- const { in_profit, in_loss } = this.total_positions_yesterday;
95
- const { profit_taken, loss_realized } = this.closed_positions_today;
96
-
97
- // Calculate rates to normalize the data
98
- const profit_taking_rate = (in_profit > 0) ? (profit_taken / in_profit) * 100 : 0;
99
- const capitulation_rate = (in_loss > 0) ? (loss_realized / in_loss) * 100 : 0;
100
-
101
- return {
102
- profit_taking_rate_pct: profit_taking_rate, // % of profitable positions that were closed
103
- capitulation_rate_pct: capitulation_rate, // % of losing positions that were closed
104
- raw_counts: {
105
- profit_positions_closed: profit_taken,
106
- loss_positions_closed: loss_realized,
107
- total_profit_positions_available: in_profit,
108
- total_loss_positions_available: in_loss
109
- }
110
- };
111
- }
112
-
113
- reset() {
114
- this.total_positions_yesterday = { in_profit: 0, in_loss: 0 };
115
- this.closed_positions_today = { profit_taken: 0, loss_realized: 0 };
116
- }
117
- }
118
-
119
- module.exports = ActivityByPnlStatus;