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
@@ -1,146 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 3) for cash flow liquidation.
3
- *
4
- * This metric answers: "Following a net withdrawal event, what percentage
5
- * of that withdrawal was funded by selling *today*, and which assets
6
- * are being liquidated the most?"
7
- *
8
- * It *depends* on 'crowd-cash-flow-proxy' (to know if it's a
9
- * withdrawal day) and 'asset_crowd_flow' (to see where flow went).
10
- */
11
- class CashFlowLiquidation {
12
- constructor() {
13
- // No per-user processing
14
- }
15
-
16
- /**
17
- * Defines the output schema for this calculation.
18
- * @returns {object} JSON Schema object
19
- */
20
- static getSchema() {
21
- const assetFlowSchema = {
22
- "type": "object",
23
- "properties": {
24
- "ticker": { "type": "string" },
25
- "net_flow_contribution": { "type": "number" },
26
- "percent_of_total_outflow": { "type": "number" }
27
- },
28
- "required": ["ticker", "net_flow_contribution", "percent_of_total_outflow"]
29
- };
30
-
31
- return {
32
- "type": "object",
33
- "description": "On net withdrawal days, tracks % of withdrawal funded by selling and which assets were sold.",
34
- "properties": {
35
- "is_net_withdrawal_day": {
36
- "type": "boolean",
37
- "description": "True if today was a net withdrawal day."
38
- },
39
- "net_cash_flow_proxy": {
40
- "type": "number",
41
- "description": "The total estimated net cash flow (negative)."
42
- },
43
- "total_net_capital_flow": {
44
- "type": "number",
45
- "description": "The sum of all *negative* net capital flows from assets (total liquidation)."
46
- },
47
- "funding_percentage": {
48
- "type": ["number", "null"],
49
- "description": "Percentage of net cash flow funded by selling (Total Net Flow / Net Cash Flow). Null if no cash flow."
50
- },
51
- "top_outflow_assets": {
52
- "type": "array",
53
- "description": "Top 5 assets being liquidated.",
54
- "items": assetFlowSchema
55
- },
56
- "asset_flow_details": {
57
- "type": "array",
58
- "description": "Full list of all assets and their outflows.",
59
- "items": assetFlowSchema
60
- }
61
- },
62
- "required": ["is_net_withdrawal_day", "net_cash_flow_proxy", "total_net_capital_flow", "funding_percentage", "top_outflow_assets", "asset_flow_details"]
63
- };
64
- }
65
-
66
- /**
67
- * Statically declare dependencies.
68
- */
69
- static getDependencies() {
70
- return [
71
- 'crowd-cash-flow-proxy', // Pass 2
72
- 'asset_crowd_flow' // Pass 2
73
- ];
74
- }
75
-
76
- process() {
77
- // No-op
78
- }
79
-
80
- getResult(fetchedDependencies) {
81
- const cashFlowData = fetchedDependencies['crowd-cash-flow-proxy'];
82
- const assetFlowData = fetchedDependencies['asset_crowd_flow'];
83
-
84
- const defaults = {
85
- is_net_withdrawal_day: false,
86
- net_cash_flow_proxy: 0,
87
- total_net_capital_flow: 0,
88
- funding_percentage: null,
89
- top_outflow_assets: [],
90
- asset_flow_details: []
91
- };
92
-
93
- if (!cashFlowData || !assetFlowData || cashFlowData.net_cash_flow_proxy >= 0) {
94
- // Not a net withdrawal day
95
- return defaults;
96
- }
97
-
98
- const netCashFlow = cashFlowData.net_cash_flow_proxy; // This is a negative number
99
- let totalNetOutflow = 0;
100
- const allFlows = [];
101
-
102
- for (const [ticker, data] of Object.entries(assetFlowData)) {
103
- // We only care about *negative* flow (liquidation)
104
- if (data.net_flow_contribution < 0) {
105
- totalNetOutflow += data.net_flow_contribution; // Summing negative numbers
106
- allFlows.push({
107
- ticker: ticker,
108
- net_flow_contribution: data.net_flow_contribution
109
- });
110
- }
111
- }
112
-
113
- if (totalNetOutflow === 0) {
114
- // Net withdrawal day, but no negative flow detected
115
- return {
116
- ...defaults,
117
- is_net_withdrawal_day: true,
118
- net_cash_flow_proxy: netCashFlow,
119
- funding_percentage: 0
120
- };
121
- }
122
-
123
- // Calculate percent_of_total_outflow for each
124
- const asset_flow_details = allFlows.map(flow => ({
125
- ...flow,
126
- // (Negative / Negative) * 100 = Positive %
127
- percent_of_total_outflow: (flow.net_flow_contribution / totalNetOutflow) * 100
128
- })).sort((a, b) => a.net_flow_contribution - b.net_flow_contribution); // Sort ascending (most negative first)
129
-
130
- return {
131
- is_net_withdrawal_day: true,
132
- net_cash_flow_proxy: netCashFlow,
133
- total_net_capital_flow: totalNetOutflow,
134
- // (Negative / Negative) * 100 = Positive %
135
- funding_percentage: (netCashFlow < 0) ? (totalNetOutflow / netCashFlow) * 100 : null,
136
- top_outflow_assets: asset_flow_details.slice(0, 5),
137
- asset_flow_details: asset_flow_details
138
- };
139
- }
140
-
141
- reset() {
142
- // No state
143
- }
144
- }
145
-
146
- module.exports = CashFlowLiquidation;
@@ -1,128 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 2) for crowd cash flow proxy.
3
- *
4
- * This metric estimates the net cash flow (deposits vs. withdrawals)
5
- * of the crowd by analyzing changes in 'Invested' vs. 'Cash' balances
6
- * in user portfolios.
7
- *
8
- * It's a proxy because it doesn't see real bank transactions, but
9
- * infers from the total portfolio value vs. the invested amount.
10
- */
11
- class CrowdCashFlowProxy {
12
- constructor() {
13
- this.total_portfolio_value_yesterday = 0;
14
- this.total_invested_yesterday = 0;
15
-
16
- this.total_portfolio_value_today = 0;
17
- this.total_invested_today = 0;
18
- }
19
-
20
- /**
21
- * Defines the output schema for this calculation.
22
- * @returns {object} JSON Schema object
23
- */
24
- static getSchema() {
25
- return {
26
- "type": "object",
27
- "description": "Estimates net cash flow (deposits/withdrawals) by comparing changes in total portfolio value vs. invested capital.",
28
- "properties": {
29
- "total_portfolio_value_yesterday": {
30
- "type": "number",
31
- "description": "Sum of all users' total portfolio values from yesterday."
32
- },
33
- "total_invested_yesterday": {
34
- "type": "number",
35
- "description": "Sum of all users' invested capital from yesterday."
36
- },
37
- "total_portfolio_value_today": {
38
- "type": "number",
39
- "description": "Sum of all users' total portfolio values from today."
40
- },
41
- "total_invested_today": {
42
- "type": "number",
43
- "description": "Sum of all users' invested capital from today."
44
- },
45
- "total_pnl_contribution": {
46
- "type": "number",
47
- "description": "The estimated change in portfolio value attributable to P&L."
48
- },
49
- "net_cash_flow_proxy": {
50
- "type": "number",
51
- "description": "The estimated net cash flow (Deposits - Withdrawals). Positive indicates net deposits."
52
- },
53
- "net_cash_flow_proxy_pct": {
54
- "type": "number",
55
- "description": "The net cash flow proxy as a percentage of yesterday's total portfolio value."
56
- }
57
- },
58
- "required": [
59
- "total_portfolio_value_yesterday", "total_invested_yesterday",
60
- "total_portfolio_value_today", "total_invested_today",
61
- "total_pnl_contribution", "net_cash_flow_proxy", "net_cash_flow_proxy_pct"
62
- ]
63
- };
64
- }
65
-
66
- _getPortfolioValues(portfolio) {
67
- if (!portfolio || !portfolio.Summary) {
68
- return { portfolioValue: 0, invested: 0 };
69
- }
70
-
71
- // Total value (Invested + Cash)
72
- const portfolioValue = portfolio.Summary.PortfolioValue || 0;
73
- // Total invested in positions
74
- const invested = portfolio.Summary.Invested || 0;
75
-
76
- return { portfolioValue, invested };
77
- }
78
-
79
- process(todayPortfolio, yesterdayPortfolio) {
80
- if (!todayPortfolio || !yesterdayPortfolio) {
81
- return;
82
- }
83
-
84
- const yValues = this._getPortfolioValues(yesterdayPortfolio);
85
- const tValues = this._getPortfolioValues(todayPortfolio);
86
-
87
- this.total_portfolio_value_yesterday += yValues.portfolioValue;
88
- this.total_invested_yesterday += yValues.invested;
89
-
90
- this.total_portfolio_value_today += tValues.portfolioValue;
91
- this.total_invested_today += tValues.invested;
92
- }
93
-
94
- getResult() {
95
- // P&L is the change in *invested* value, not total value
96
- const totalPnl = this.total_invested_today - this.total_invested_yesterday;
97
-
98
- // The change in total value is (P&L + Net Cash Flow)
99
- // So, Net Cash Flow = (Total Value Change) - P&L
100
- const totalValueChange = this.total_portfolio_value_today - this.total_portfolio_value_yesterday;
101
-
102
- const netCashFlowProxy = totalValueChange - totalPnl;
103
-
104
- const netCashFlowProxyPct = (this.total_portfolio_value_yesterday > 0)
105
- ? (netCashFlowProxy / this.total_portfolio_value_yesterday) * 100
106
- : 0;
107
-
108
- return {
109
- total_portfolio_value_yesterday: this.total_portfolio_value_yesterday,
110
- total_invested_yesterday: this.total_invested_yesterday,
111
- total_portfolio_value_today: this.total_portfolio_value_today,
112
- total_invested_today: this.total_invested_today,
113
-
114
- total_pnl_contribution: totalPnl,
115
- net_cash_flow_proxy: netCashFlowProxy,
116
- net_cash_flow_proxy_pct: netCashFlowProxyPct
117
- };
118
- }
119
-
120
- reset() {
121
- this.total_portfolio_value_yesterday = 0;
122
- this.total_invested_yesterday = 0;
123
- this.total_portfolio_value_today = 0;
124
- this.total_invested_today = 0;
125
- }
126
- }
127
-
128
- module.exports = CrowdCashFlowProxy;
@@ -1,261 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 2) for crowd conviction.
3
- *
4
- * This metric answers: "What is the 'Crowd Conviction Score'
5
- * for each instrument?"
6
- *
7
- * It's based on factors like:
8
- * 1. Holding Duration (longer = more conviction)
9
- * 2. P&L % (positive = more conviction)
10
- * 3. Risk/Reward Ratio (higher = more conviction)
11
- * 4. Leverage (lower = more conviction)
12
- *
13
- * This is a *stateful* calculation that computes a 30-day
14
- * rolling average of these metrics to build the score.
15
- */
16
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
17
-
18
- class CrowdConvictionScore {
19
- constructor() {
20
- // { [instrumentId]: { metrics: [], history: [] } }
21
- this.assetData = new Map();
22
- this.mappings = null;
23
- }
24
-
25
- /**
26
- * Defines the output schema for this calculation.
27
- * @returns {object} JSON Schema object
28
- */
29
- static getSchema() {
30
- const historyItemSchema = {
31
- "type": "object",
32
- "properties": {
33
- "avg_holding_duration": { "type": "number" },
34
- "avg_pnl": { "type": "number" },
35
- "avg_rr": { "type": "number" },
36
- "avg_leverage": { "type": "number" }
37
- },
38
- "required": ["avg_holding_duration", "avg_pnl", "avg_rr", "avg_leverage"]
39
- };
40
-
41
- const tickerSchema = {
42
- "type": "object",
43
- "description": "Crowd conviction score and its components for a specific asset.",
44
- "properties": {
45
- "conviction_score": {
46
- "type": "number",
47
- "description": "Final conviction score (0-100), based on 30-day rolling averages."
48
- },
49
- "roll_avg_holding_duration": {
50
- "type": "number",
51
- "description": "30-day rolling average of holding duration (hours)."
52
- },
53
- "roll_avg_pnl": {
54
- "type": "number",
55
- "description": "30-day rolling average of P&L percentage."
56
- },
57
- "roll_avg_rr": {
58
- "type": "number",
59
- "description": "30-day rolling average of risk/reward ratio."
60
- },
61
- "roll_avg_leverage": {
62
- "type": "number",
63
- "description": "30-day rolling average of leverage."
64
- },
65
- "avg_holding_duration": {
66
- "type": "number",
67
- "description": "Today's average holding duration."
68
- },
69
- "avg_pnl": {
70
- "type": "number",
71
- "description": "Today's average P&L percentage."
72
- },
73
- "avg_rr": {
74
- "type": "number",
75
- "description": "Today's average risk/reward ratio."
76
- },
77
- "avg_leverage": {
78
- "type": "number",
79
- "description": "Today's average leverage."
80
- },
81
- "history": {
82
- "type": "array",
83
- "description": "30-day history of daily average metrics.",
84
- "items": historyItemSchema
85
- }
86
- },
87
- "required": [
88
- "conviction_score", "roll_avg_holding_duration", "roll_avg_pnl",
89
- "roll_avg_rr", "roll_avg_leverage", "avg_holding_duration",
90
- "avg_pnl", "avg_rr", "avg_leverage", "history"
91
- ]
92
- };
93
-
94
- return {
95
- "type": "object",
96
- "description": "Calculates a 30-day rolling 'Crowd Conviction Score' (0-100) for each asset.",
97
- "patternProperties": {
98
- "^.*$": tickerSchema // Ticker
99
- },
100
- "additionalProperties": tickerSchema
101
- };
102
- }
103
-
104
- _initAsset(instrumentId) {
105
- if (!this.assetData.has(instrumentId)) {
106
- // metrics: Stores *today's* raw values before averaging
107
- // history: Stores *yesterday's* 30-day history
108
- this.assetData.set(instrumentId, {
109
- metrics: [],
110
- history: []
111
- });
112
- }
113
- }
114
-
115
- _getHoldingDurationHours(openDateStr) {
116
- if (!openDateStr) return 0;
117
- try {
118
- const openDate = new Date(openDateStr);
119
- const diffMs = new Date().getTime() - openDate.getTime();
120
- return diffMs / (1000 * 60 * 60);
121
- } catch (e) {
122
- return 0;
123
- }
124
- }
125
-
126
- // Simple min-max normalization helper
127
- _normalize(value, min, max) {
128
- if (max === min) return 0.5; // Avoid division by zero
129
- const normalized = (value - min) / (max - min);
130
- return Math.max(0, Math.min(1, normalized)); // Clamp between 0 and 1
131
- }
132
-
133
- process(portfolioData, yesterdayPortfolio, userId, context) {
134
- // This score is based on speculator actions
135
- if (portfolioData?.context?.userType !== 'speculator') {
136
- return;
137
- }
138
-
139
- // 1. Get this metric's history from yesterday (pre-loaded)
140
- if (this.assetData.size === 0) { // Only run once
141
- const yHistoryData = context.yesterdaysDependencyData['crowd_conviction_score'];
142
- if (yHistoryData) {
143
- if (!this.mappings) {
144
- // We need mappings to convert *yesterday's* tickers back to IDs
145
- this.mappings = context.mappings;
146
- }
147
- for (const [ticker, data] of Object.entries(yHistoryData)) {
148
- const instrumentId = this.mappings.tickerToInstrument[ticker];
149
- if (instrumentId) {
150
- this._initAsset(instrumentId);
151
- this.assetData.get(instrumentId).history = data.history || [];
152
- }
153
- }
154
- }
155
- }
156
-
157
- const positions = portfolioData.PublicPositions;
158
- if (!positions || !Array.isArray(positions)) {
159
- return;
160
- }
161
-
162
- for (const pos of positions) {
163
- const instrumentId = pos.InstrumentID;
164
- if (!instrumentId) continue;
165
-
166
- this._initAsset(instrumentId);
167
-
168
- const pnl_percent = (pos.NetProfit || 0) / (pos.Amount || 1);
169
- const holding_duration = this._getHoldingDurationHours(pos.OpenDateTime);
170
- const leverage = pos.Leverage || 1;
171
-
172
- const sl_rate = pos.StopLossRate || 0;
173
- const tp_rate = pos.TakeProfitRate || 0;
174
- const open_rate = pos.OpenRate || 1;
175
-
176
- let rr_ratio = 0;
177
- if (sl_rate > 0 && tp_rate > 0) {
178
- const risk = Math.abs(open_rate - sl_rate);
179
- const reward = Math.abs(tp_rate - open_rate);
180
- if (risk > 0) {
181
- rr_ratio = reward / risk;
182
- }
183
- }
184
-
185
- this.assetData.get(instrumentId).metrics.push({
186
- holding_duration,
187
- pnl_percent,
188
- rr_ratio,
189
- leverage
190
- });
191
- }
192
- }
193
-
194
- async getResult() {
195
- if (!this.mappings) {
196
- this.mappings = await loadInstrumentMappings();
197
- }
198
-
199
- const result = {};
200
- for (const [instrumentId, data] of this.assetData.entries()) {
201
- const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
202
- const metrics = data.metrics;
203
- const yHistory = data.history; // Yesterday's 30-day history
204
-
205
- if (metrics.length === 0) continue;
206
-
207
- // 1. Calculate today's averages
208
- const count = metrics.length;
209
- const avg_holding_duration = metrics.reduce((s, m) => s + m.holding_duration, 0) / count;
210
- const avg_pnl = metrics.reduce((s, m) => s + m.pnl_percent, 0) / count;
211
- const avg_rr = metrics.reduce((s, m) => s + m.rr_ratio, 0) / count;
212
- const avg_leverage = metrics.reduce((s, m) => s + m.leverage, 0) / count;
213
-
214
- const todayMetrics = { avg_holding_duration, avg_pnl, avg_rr, avg_leverage };
215
-
216
- // 2. Create new 30-day history
217
- const newHistory = [todayMetrics, ...yHistory].slice(0, 30);
218
-
219
- // 3. Calculate 30-day rolling averages
220
- const historyCount = newHistory.length;
221
- const roll_avg_duration = newHistory.reduce((s, m) => s + m.avg_holding_duration, 0) / historyCount;
222
- const roll_avg_pnl = newHistory.reduce((s, m) => s + m.avg_pnl, 0) / historyCount;
223
- const roll_avg_rr = newHistory.reduce((s, m) => s + m.avg_rr, 0) / historyCount;
224
- const roll_avg_leverage = newHistory.reduce((s, m) => s + m.avg_leverage, 0) / historyCount;
225
-
226
- // 4. Calculate Conviction Score (example normalization)
227
- // (Assumes a 0-1 range for normalization, then scales to 0-100)
228
- const norm_duration = this._normalize(roll_avg_duration, 0, 240); // 0-10 days
229
- const norm_pnl = this._normalize(roll_avg_pnl, -0.5, 0.5); // -50% to +50%
230
- const norm_rr = this._normalize(roll_avg_rr, 0, 3); // 0 to 3 R:R
231
- const norm_leverage = 1 - this._normalize(roll_avg_leverage, 1, 10); // 1x to 10x (inverted)
232
-
233
- // Combine scores (equal weight for this example)
234
- const score_pct = (norm_duration + norm_pnl + norm_rr + norm_leverage) / 4;
235
-
236
- result[ticker] = {
237
- conviction_score: score_pct * 100, // Final score 0-100
238
- roll_avg_holding_duration,
239
- roll_avg_pnl,
240
- roll_avg_rr,
241
- roll_avg_leverage,
242
-
243
- // Also include today's raw averages
244
- avg_holding_duration,
245
- avg_pnl,
246
- avg_rr,
247
- avg_leverage,
248
-
249
- history: newHistory
250
- };
251
- }
252
- return result;
253
- }
254
-
255
- reset() {
256
- this.assetData.clear();
257
- this.mappings = null;
258
- }
259
- }
260
-
261
- module.exports = CrowdConvictionScore;
@@ -1,137 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 2) for crowd sharpe ratio proxy.
3
- *
4
- * This metric answers: "What is the crowd's risk-adjusted return
5
- * (Sharpe Ratio proxy) for each asset?"
6
- *
7
- * It uses the distribution of P&L from 'pnl_distribution_per_stock'
8
- * to calculate variance (risk).
9
- *
10
- * --- FIX: 2025-11-12 ---
11
- * Refactored this file to be a "meta" calculation.
12
- * 1. Removed constructor, getResult, reset, and the no-op 'process'.
13
- * 2. Added the required `async process(dStr, deps, config, fetchedDeps)` method.
14
- * 3. Moved all logic into `process`.
15
- * 4. Updated logic to read from `fetchedDeps['pnl_distribution_per_stock']`.
16
- * 5. Updated data access to read from the new `data.stats` object
17
- * provided by the fixed dependency.
18
- */
19
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
20
-
21
- class CrowdSharpeRatioProxy {
22
- /**
23
- * Defines the output schema for this calculation.
24
- * @returns {object} JSON Schema object
25
- */
26
- static getSchema() {
27
- const tickerSchema = {
28
- "type": "object",
29
- "description": "Sharpe Ratio proxy metrics for a specific asset.",
30
- "properties": {
31
- "sharpe_ratio_proxy": {
32
- "type": "number",
33
- "description": "Risk-adjusted return proxy (Mean P&L / StdDev P&L). Assumes risk-free rate is 0."
34
- },
35
- "average_pnl": { "type": "number" },
36
- "std_dev_pnl": { "type": "number" },
37
- "variance_pnl": { "type": "number" },
38
- "position_count": { "type": "number" }
39
- },
40
- "required": ["sharpe_ratio_proxy", "average_pnl", "std_dev_pnl", "position_count"]
41
- };
42
-
43
- return {
44
- "type": "object",
45
- "description": "Calculates a risk-adjusted return (Sharpe Ratio proxy) for each asset based on P&L distribution.",
46
- "patternProperties": {
47
- "^.*$": tickerSchema // Matches any string key (ticker)
48
- },
49
- "additionalProperties": tickerSchema
50
- };
51
- }
52
-
53
- /**
54
- * Statically declare dependencies.
55
- */
56
- static getDependencies() {
57
- return [
58
- 'pnl_distribution_per_stock' // Pass 1
59
- ];
60
- }
61
-
62
- /**
63
- * --- FIX: This is the new main execution method for meta-calcs ---
64
- * It receives all dependencies from the orchestrator.
65
- */
66
- async process(dateStr, dependencies, config, fetchedDependencies) {
67
- // --- FIX: Load dependency data from the argument ---
68
- const pnlDistData = fetchedDependencies['pnl_distribution_per_stock'];
69
-
70
- if (!pnlDistData) {
71
- dependencies.logger.log('WARN', `[crowd_sharpe_ratio_proxy] Missing dependency 'pnl_distribution_per_stock' for ${dateStr}. Skipping.`);
72
- return {};
73
- }
74
-
75
- // --- FIX: Load mappings inside the process method ---
76
- const mappings = await loadInstrumentMappings();
77
- if (!mappings || !mappings.instrumentToTicker) {
78
- dependencies.logger.log('ERROR', `[crowd_sharpe_ratio_proxy] Failed to load instrument mappings.`);
79
- return {};
80
- }
81
-
82
- const result = {};
83
-
84
- for (const [ticker, data] of Object.entries(pnlDistData)) {
85
-
86
- // --- FIX: Read from the new 'stats' sub-object ---
87
- if (!data.stats) {
88
- continue; // Skip if data is malformed
89
- }
90
-
91
- const { sum, sumSq, count } = data.stats;
92
-
93
- if (count < 2) {
94
- continue; // Need at least 2 data points for variance
95
- }
96
-
97
- // Calculate Mean (Average P&L)
98
- const mean = sum / count;
99
-
100
- // Calculate Variance
101
- // Var(X) = E[X^2] - (E[X])^2
102
- const meanSq = sumSq / count;
103
- const variance = meanSq - (mean * mean);
104
-
105
- // Handle potential float precision errors
106
- if (variance < 0) {
107
- continue;
108
- }
109
-
110
- // Calculate Standard Deviation (Risk)
111
- const stdDev = Math.sqrt(variance);
112
-
113
- if (stdDev === 0) {
114
- continue; // No risk, Sharpe ratio is infinite/undefined
115
- }
116
-
117
- // Calculate Sharpe Ratio Proxy (assuming risk-free rate = 0)
118
- // Sharpe = Mean(Return) / StdDev(Return)
119
- const sharpeProxy = mean / stdDev;
120
-
121
- // --- FIX: Data is already keyed by ticker, no mapping needed ---
122
- // const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
123
-
124
- result[ticker] = {
125
- sharpe_ratio_proxy: sharpeProxy,
126
- average_pnl: mean,
127
- std_dev_pnl: stdDev,
128
- variance_pnl: variance,
129
- position_count: count
130
- };
131
- }
132
-
133
- return result;
134
- }
135
- }
136
-
137
- module.exports = CrowdSharpeRatioProxy;