aiden-shared-calculations-unified 1.0.95 → 1.0.96

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 (54) 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/ghost-book/cost-basis-density.js +1 -2
  4. package/calculations/ghost-book/liquidity-vacuum.js +1 -2
  5. package/calculations/ghost-book/retail-gamma-exposure.js +0 -1
  6. package/calculations/predicative-alpha/cognitive-dissonance.js +1 -2
  7. package/calculations/predicative-alpha/diamond-hand-fracture.js +1 -2
  8. package/calculations/predicative-alpha/mimetic-latency.js +1 -2
  9. package/package.json +1 -1
  10. package/calculations/legacy/activity_by_pnl_status.js +0 -119
  11. package/calculations/legacy/asset_crowd_flow.js +0 -163
  12. package/calculations/legacy/capital_deployment_strategy.js +0 -108
  13. package/calculations/legacy/capital_liquidation_performance.js +0 -139
  14. package/calculations/legacy/capital_vintage_performance.js +0 -136
  15. package/calculations/legacy/cash-flow-deployment.js +0 -144
  16. package/calculations/legacy/cash-flow-liquidation.js +0 -146
  17. package/calculations/legacy/crowd-cash-flow-proxy.js +0 -128
  18. package/calculations/legacy/crowd_conviction_score.js +0 -261
  19. package/calculations/legacy/crowd_sharpe_ratio_proxy.js +0 -137
  20. package/calculations/legacy/daily_asset_activity.js +0 -128
  21. package/calculations/legacy/daily_user_activity_tracker.js +0 -182
  22. package/calculations/legacy/deposit_withdrawal_percentage.js +0 -125
  23. package/calculations/legacy/diversification_pnl.js +0 -115
  24. package/calculations/legacy/drawdown_response.js +0 -137
  25. package/calculations/legacy/dumb-cohort-flow.js +0 -238
  26. package/calculations/legacy/gain_response.js +0 -137
  27. package/calculations/legacy/historical_performance_aggregator.js +0 -85
  28. package/calculations/legacy/in_loss_asset_crowd_flow.js +0 -168
  29. package/calculations/legacy/in_profit_asset_crowd_flow.js +0 -168
  30. package/calculations/legacy/negative_expectancy_cohort_flow.js +0 -232
  31. package/calculations/legacy/new_allocation_percentage.js +0 -98
  32. package/calculations/legacy/paper_vs_diamond_hands.js +0 -107
  33. package/calculations/legacy/position_count_pnl.js +0 -120
  34. package/calculations/legacy/positive_expectancy_cohort_flow.js +0 -232
  35. package/calculations/legacy/profit_cohort_divergence.js +0 -115
  36. package/calculations/legacy/profitability_migration.js +0 -104
  37. package/calculations/legacy/reallocation_increase_percentage.js +0 -104
  38. package/calculations/legacy/risk_appetite_change.js +0 -97
  39. package/calculations/legacy/sector_rotation.js +0 -117
  40. package/calculations/legacy/shark_attack_signal.js +0 -112
  41. package/calculations/legacy/smart-cohort-flow.js +0 -238
  42. package/calculations/legacy/smart-dumb-divergence-index.js +0 -143
  43. package/calculations/legacy/smart_dumb_divergence_index_v2.js +0 -138
  44. package/calculations/legacy/smart_money_flow.js +0 -198
  45. package/calculations/legacy/social-predictive-regime-state.js +0 -102
  46. package/calculations/legacy/social-topic-driver-index.js +0 -147
  47. package/calculations/legacy/social-topic-predictive-potential.js +0 -461
  48. package/calculations/legacy/social_flow_correlation.js +0 -112
  49. package/calculations/legacy/speculator_adjustment_activity.js +0 -103
  50. package/calculations/legacy/strategy-performance.js +0 -265
  51. package/calculations/legacy/tsl_effectiveness.js +0 -85
  52. package/calculations/legacy/user-investment-profile.js +0 -313
  53. package/calculations/legacy/user_expectancy_score.js +0 -106
  54. 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
 
@@ -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
 
@@ -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
 
@@ -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.96",
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;
@@ -1,163 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 2) for asset crowd flow.
3
- *
4
- * This metric calculates the "Net Crowd Flow Percentage" for each asset.
5
- * It's defined as the net percentage of the crowd's capital flowing in or out
6
- * of an asset, *adjusted for the asset's own price movement*.
7
- *
8
- * This isolates true buying/selling pressure from simple value changes.
9
- */
10
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
11
-
12
- class AssetCrowdFlow {
13
- constructor() {
14
- this.assetData = new Map();
15
- this.mappings = null;
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": "Calculates the net capital flow % per asset, adjusted for price movements.",
26
- "patternProperties": {
27
- // Ticker
28
- "^.*$": {
29
- "type": "object",
30
- "description": "Net crowd flow metrics for a specific asset ticker.",
31
- "properties": {
32
- "net_flow_percentage": {
33
- "type": "number",
34
- "description": "Net capital flow (buying/selling) as a percentage of total holdings, adjusted for price changes."
35
- },
36
- "total_invested_today": {
37
- "type": "number",
38
- "description": "Total USD value invested in this asset today by the sample."
39
- },
40
- "total_invested_yesterday": {
41
- "type": "number",
42
- "description": "Total USD value invested in this asset yesterday by the sample."
43
- },
44
- "price_change_contribution": {
45
- "type": "number",
46
- "description": "The portion of the value change attributable to the asset's price movement."
47
- },
48
- "net_flow_contribution": {
49
- "type": "number",
50
- "description": "The portion of the value change attributable to net buying or selling."
51
- }
52
- },
53
- "required": ["net_flow_percentage", "total_invested_today", "total_invested_yesterday"]
54
- }
55
- },
56
- "additionalProperties": {
57
- "type": "object",
58
- "properties": {
59
- "net_flow_percentage": { "type": "number" },
60
- "total_invested_today": { "type": "number" },
61
- "total_invested_yesterday": { "type": "number" },
62
- "price_change_contribution": { "type": "number" },
63
- "net_flow_contribution": { "type": "number" }
64
- }
65
- }
66
- };
67
- }
68
-
69
- _getPortfolioPositions(portfolio) {
70
- return portfolio?.PublicPositions || portfolio?.AggregatedPositions;
71
- }
72
-
73
- _initAsset(instrumentId) {
74
- if (!this.assetData.has(instrumentId)) {
75
- this.assetData.set(instrumentId, {
76
- total_invested_yesterday: 0,
77
- total_invested_today: 0,
78
- price_change_yesterday: 0, // Used to calculate weighted avg price change
79
- });
80
- }
81
- }
82
-
83
- process(todayPortfolio, yesterdayPortfolio) {
84
- if (!todayPortfolio || !yesterdayPortfolio) {
85
- return;
86
- }
87
-
88
- const yPos = this._getPortfolioPositions(yesterdayPortfolio);
89
- const tPos = this._getPortfolioPositions(todayPortfolio);
90
-
91
- const yPosMap = new Map(yPos?.map(p => [p.InstrumentID, p]) || []);
92
- const tPosMap = new Map(tPos?.map(p => [p.InstrumentID, p]) || []);
93
-
94
- const allInstrumentIds = new Set([...yPosMap.keys(), ...tPosMap.keys()]);
95
-
96
- for (const instrumentId of allInstrumentIds) {
97
- if (!instrumentId) continue;
98
-
99
- this._initAsset(instrumentId);
100
- const asset = this.assetData.get(instrumentId);
101
-
102
- const yP = yPosMap.get(instrumentId);
103
- const tP = tPosMap.get(instrumentId);
104
-
105
- const yInvested = yP?.Invested || 0;
106
- const tInvested = tP?.Invested || 0;
107
-
108
- if (yInvested > 0) {
109
- asset.total_invested_yesterday += yInvested;
110
- // Get price change from *yesterday's* portfolio
111
- const yPriceChange = (yP?.PipsRate || 0) / (yP?.OpenRate || 1); // PipsRate holds 1-day change
112
- asset.price_change_yesterday += yPriceChange * yInvested; // Weighted sum
113
- }
114
- if (tInvested > 0) {
115
- asset.total_invested_today += tInvested;
116
- }
117
- }
118
- }
119
-
120
- async getResult() {
121
- if (!this.mappings) {
122
- this.mappings = await loadInstrumentMappings();
123
- }
124
-
125
- const result = {};
126
-
127
- for (const [instrumentId, data] of this.assetData.entries()) {
128
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
129
-
130
- const { total_invested_yesterday, total_invested_today, price_change_yesterday } = data;
131
-
132
- if (total_invested_yesterday > 0) {
133
- // Calculate the weighted average price change %
134
- const avg_price_change_pct = price_change_yesterday / total_invested_yesterday;
135
-
136
- // Estimate the value change *due to price*
137
- const price_contribution = total_invested_yesterday * avg_price_change_pct;
138
-
139
- // Estimate the value change *due to net flow* (buy/sell)
140
- const flow_contribution = total_invested_today - (total_invested_yesterday + price_contribution);
141
-
142
- // Calculate Net Flow as a percentage of yesterday's holdings
143
- const net_flow_percentage = (flow_contribution / total_invested_yesterday) * 100;
144
-
145
- result[ticker] = {
146
- net_flow_percentage: net_flow_percentage,
147
- total_invested_today: total_invested_today,
148
- total_invested_yesterday: total_invested_yesterday,
149
- price_change_contribution: price_contribution,
150
- net_flow_contribution: flow_contribution
151
- };
152
- }
153
- }
154
- return result;
155
- }
156
-
157
- reset() {
158
- this.assetData.clear();
159
- this.mappings = null;
160
- }
161
- }
162
-
163
- module.exports = AssetCrowdFlow;
@@ -1,108 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 3) for capital deployment strategy.
3
- *
4
- * This metric answers: "Following a net deposit event, does the crowd
5
- * tend to deploy new capital into new assets or add to existing ones?"
6
- *
7
- * It *depends* on 'crowd-cash-flow-proxy', 'new_allocation_percentage',
8
- * and 'reallocation_increase_percentage'.
9
- */
10
- class CapitalDeploymentStrategy {
11
- constructor() {
12
- // This class only aggregates results from other calculations.
13
- // No per-user processing is needed.
14
- }
15
-
16
- /**
17
- * Defines the output schema for this calculation.
18
- * @returns {object} JSON Schema object
19
- */
20
- static getSchema() {
21
- return {
22
- "type": "object",
23
- "description": "Analyzes if the crowd deploys new capital into new or existing assets, triggered by a net deposit event.",
24
- "properties": {
25
- "is_net_deposit_day": {
26
- "type": "boolean",
27
- "description": "True if today was a net deposit day for the crowd."
28
- },
29
- "net_cash_flow_proxy": {
30
- "type": "number",
31
- "description": "The estimated net cash flow (from 'crowd-cash-flow-proxy')."
32
- },
33
- "total_deployed_to_new_positions_pct": {
34
- "type": "number",
35
- "description": "The total % portfolio allocation deployed to new positions (from 'new_allocation_percentage')."
36
- },
37
- "total_deployed_to_existing_positions_pct": {
38
- "type": "number",
39
- "description": "The total % portfolio allocation added to existing positions (from 'reallocation_increase_percentage')."
40
- },
41
- "deployment_ratio_new_vs_existing": {
42
- "type": ["number", "null"],
43
- "description": "Ratio of (New / Existing) deployment. Null if no deployment to existing positions."
44
- }
45
- },
46
- "required": ["is_net_deposit_day", "net_cash_flow_proxy", "total_deployed_to_new_positions_pct", "total_deployed_to_existing_positions_pct", "deployment_ratio_new_vs_existing"]
47
- };
48
- }
49
-
50
- /**
51
- * Statically declare dependencies.
52
- */
53
- static getDependencies() {
54
- return [
55
- 'crowd-cash-flow-proxy',
56
- 'new_allocation_percentage',
57
- 'reallocation_increase_percentage'
58
- ];
59
- }
60
-
61
- /**
62
- * process() is a no-op. All logic is in getResult().
63
- */
64
- process() {
65
- // No-op
66
- }
67
-
68
- /**
69
- * Aggregates results from dependencies.
70
- */
71
- getResult(fetchedDependencies) {
72
- const cashFlowData = fetchedDependencies['crowd-cash-flow-proxy'];
73
- const newAllocData = fetchedDependencies['new_allocation_percentage'];
74
- const reallocData = fetchedDependencies['reallocation_increase_percentage'];
75
-
76
- if (!cashFlowData || !newAllocData || !reallocData) {
77
- // Return a default "non-event" state if dependencies are missing
78
- return {
79
- is_net_deposit_day: false,
80
- net_cash_flow_proxy: 0,
81
- total_deployed_to_new_positions_pct: 0,
82
- total_deployed_to_existing_positions_pct: 0,
83
- deployment_ratio_new_vs_existing: null
84
- };
85
- }
86
-
87
- const netCashFlow = cashFlowData.net_cash_flow_proxy || 0;
88
- const isNetDepositDay = netCashFlow > 0;
89
-
90
- // We only report deployment stats IF it was a deposit day.
91
- const newAllocSum = isNetDepositDay ? newAllocData.total_new_allocation_pct_sum : 0;
92
- const reallocSum = isNetDepositDay ? reallocData.total_reallocation_increase_pct_sum : 0;
93
-
94
- return {
95
- is_net_deposit_day: isNetDepositDay,
96
- net_cash_flow_proxy: netCashFlow,
97
- total_deployed_to_new_positions_pct: newAllocSum,
98
- total_deployed_to_existing_positions_pct: reallocSum,
99
- deployment_ratio_new_vs_existing: (reallocSum > 0) ? (newAllocSum / reallocSum) : null
100
- };
101
- }
102
-
103
- reset() {
104
- // No state to reset
105
- }
106
- }
107
-
108
- module.exports = CapitalDeploymentStrategy;
@@ -1,139 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 4) for capital liquidation performance.
3
- *
4
- * This metric answers: "When users liquidate assets (net withdrawal),
5
- * do they tend to sell winners or losers?"
6
- *
7
- * It *depends* on 'cash-flow-liquidation' (to get the list of
8
- * liquidated assets) and 'asset_pnl_status' (to check the P&L
9
- * status of those assets).
10
- */
11
- class CapitalLiquidationPerformance {
12
- constructor() {
13
- // No per-user processing needed
14
- }
15
-
16
- /**
17
- * Defines the output schema for this calculation.
18
- * @returns {object} JSON Schema object
19
- */
20
- static getSchema() {
21
- return {
22
- "type": "object",
23
- "description": "Analyzes if users sell winners or losers during net withdrawal events.",
24
- "properties": {
25
- "is_net_withdrawal_day": {
26
- "type": "boolean",
27
- "description": "True if today was a net withdrawal day."
28
- },
29
- "total_assets_liquidated": {
30
- "type": "number",
31
- "description": "The number of assets that saw net liquidation."
32
- },
33
- "assets_sold_at_profit_count": {
34
- "type": "number",
35
- "description": "Count of liquidated assets where the majority of users were in profit."
36
- },
37
- "assets_sold_at_loss_count": {
38
- "type": "number",
39
- "description": "Count of liquidated assets where the majority of users were in loss."
40
- },
41
- "winner_loser_sell_ratio": {
42
- "type": ["number", "null"],
43
- "description": "Ratio of (Assets Sold at Profit / Assets Sold at Loss). Null if no assets sold at loss."
44
- },
45
- "details": {
46
- "type": "array",
47
- "description": "List of liquidated assets and their P&L status.",
48
- "items": {
49
- "type": "object",
50
- "properties": {
51
- "ticker": { "type": "string" },
52
- "net_flow_contribution": { "type": "number" },
53
- "status": { "type": "string", "enum": ["profit", "loss", "neutral"] },
54
- "profit_ratio": { "type": "number" }
55
- },
56
- "required": ["ticker", "net_flow_contribution", "status", "profit_ratio"]
57
- }
58
- }
59
- },
60
- "required": ["is_net_withdrawal_day", "total_assets_liquidated", "assets_sold_at_profit_count", "assets_sold_at_loss_count", "winner_loser_sell_ratio", "details"]
61
- };
62
- }
63
-
64
- /**
65
- * Statically declare dependencies.
66
- */
67
- static getDependencies() {
68
- return [
69
- 'cash-flow-liquidation', // Pass 3
70
- 'asset_pnl_status' // Pass 1
71
- ];
72
- }
73
-
74
- process() {
75
- // No-op
76
- }
77
-
78
- getResult(fetchedDependencies) {
79
- const liquidationData = fetchedDependencies['cash-flow-liquidation'];
80
- const pnlStatusData = fetchedDependencies['asset_pnl_status'];
81
-
82
- const defaults = {
83
- is_net_withdrawal_day: false,
84
- total_assets_liquidated: 0,
85
- assets_sold_at_profit_count: 0,
86
- assets_sold_at_loss_count: 0,
87
- winner_loser_sell_ratio: null,
88
- details: []
89
- };
90
-
91
- if (!liquidationData || !pnlStatusData || !liquidationData.is_net_withdrawal_day) {
92
- return defaults;
93
- }
94
-
95
- let profitCount = 0;
96
- let lossCount = 0;
97
- const details = [];
98
-
99
- for (const asset of liquidationData.asset_flow_details) {
100
- // We only care about assets that were *sold* (negative flow)
101
- if (asset.net_flow_contribution < 0) {
102
- const ticker = asset.ticker;
103
- const pnlInfo = pnlStatusData[ticker];
104
-
105
- if (pnlInfo) {
106
- let status = 'neutral';
107
- if (pnlInfo.profit_ratio > 50) {
108
- status = 'profit';
109
- profitCount++;
110
- } else if (pnlInfo.profit_ratio < 50) {
111
- status = 'loss';
112
- lossCount++;
113
- }
114
- details.push({
115
- ticker: ticker,
116
- net_flow_contribution: asset.net_flow_contribution,
117
- status: status,
118
- profit_ratio: pnlInfo.profit_ratio
119
- });
120
- }
121
- }
122
- }
123
-
124
- return {
125
- is_net_withdrawal_day: true,
126
- total_assets_liquidated: details.length,
127
- assets_sold_at_profit_count: profitCount,
128
- assets_sold_at_loss_count: lossCount,
129
- winner_loser_sell_ratio: (lossCount > 0) ? (profitCount / lossCount) : null,
130
- details: details
131
- };
132
- }
133
-
134
- reset() {
135
- // No state
136
- }
137
- }
138
-
139
- module.exports = CapitalLiquidationPerformance;