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,143 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 4) for smart-dumb divergence index.
3
- *
4
- * This metric answers: "What divergence signals (e.g., capitulation,
5
- * euphoria) can be found by comparing the net asset and sector flow
6
- * of the 'smart cohort' vs. the 'dumb cohort'?"
7
- *
8
- * It *depends* on 'smart-cohort-flow' and 'dumb-cohort-flow'.
9
- */
10
- class SmartDumbDivergenceIndex {
11
- constructor() {
12
- // No per-user processing
13
- }
14
-
15
- /**
16
- * Defines the output schema for this calculation.
17
- * @returns {object} JSON Schema object
18
- */
19
- static getSchema() {
20
- const signalSchema = {
21
- "type": "object",
22
- "properties": {
23
- "status": {
24
- "type": "string",
25
- "enum": ["Capitulation", "Euphoria", "Confirmation (Buy)", "Confirmation (Sell)", "Divergence (Smart Buy)", "Divergence (Smart Sell)", "Neutral"]
26
- },
27
- "smart_flow_pct": { "type": "number" },
28
- "dumb_flow_pct": { "type": "number" }
29
- },
30
- "required": ["status", "smart_flow_pct", "dumb_flow_pct"]
31
- };
32
-
33
- return {
34
- "type": "object",
35
- "description": "Generates divergence signals by comparing net flow of 'Smart' vs. 'Dumb' cohorts, by asset and sector.",
36
- "properties": {
37
- "assets": {
38
- "type": "object",
39
- "description": "Divergence signals per asset.",
40
- "patternProperties": { "^.*$": signalSchema }, // Ticker
41
- "additionalProperties": signalSchema
42
- },
43
- "sectors": {
44
- "type": "object",
45
- "description": "Divergence signals per sector.",
46
- "patternProperties": { "^.*$": signalSchema }, // Sector
47
- "additionalProperties": signalSchema
48
- }
49
- },
50
- "required": ["assets", "sectors"]
51
- };
52
- }
53
-
54
- /**
55
- * Statically declare dependencies.
56
- */
57
- static getDependencies() {
58
- return [
59
- 'smart-cohort-flow', // Pass 3
60
- 'dumb-cohort-flow' // Pass 3
61
- ];
62
- }
63
-
64
- process() {
65
- // No-op
66
- }
67
-
68
- _calculateDivergence(smartFlow, dumbFlow) {
69
- const result = {};
70
- if (!smartFlow || !dumbFlow) {
71
- return result;
72
- }
73
-
74
- const allKeys = new Set([...Object.keys(smartFlow), ...Object.keys(dumbFlow)]);
75
- const THRESHOLD = 1; // Min flow %
76
-
77
- for (const key of allKeys) {
78
- const sFlow = smartFlow[key]?.net_flow_percentage || 0;
79
- const dFlow = dumbFlow[key]?.net_flow_percentage || 0;
80
-
81
- let status = 'Neutral';
82
-
83
- // Both buying
84
- if (sFlow > THRESHOLD && dFlow > THRESHOLD) {
85
- status = 'Confirmation (Buy)';
86
- }
87
- // Both selling
88
- else if (sFlow < -THRESHOLD && dFlow < -THRESHOLD) {
89
- status = 'Confirmation (Sell)';
90
- }
91
- // Smart buying, Dumb selling
92
- else if (sFlow > THRESHOLD && dFlow < -THRESHOLD) {
93
- status = 'Capitulation'; // Smart buying the dip from dumb money
94
- }
95
- // Smart selling, Dumb buying
96
- else if (sFlow < -THRESHOLD && dFlow > THRESHOLD) {
97
- status = 'Euphoria'; // Smart selling into dumb money fomo
98
- }
99
- // Smart buying, Dumb neutral
100
- else if (sFlow > THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
101
- status = 'Divergence (Smart Buy)';
102
- }
103
- // Smart selling, Dumb neutral
104
- else if (sFlow < -THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
105
- status = 'Divergence (Smart Sell)';
106
- }
107
- // Dumb buying, Smart neutral
108
- else if (dFlow > THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
109
- status = 'Divergence (Smart Sell)'; // Implied smart sell
110
- }
111
- // Dumb selling, Smart neutral
112
- else if (dFlow < -THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
113
- status = 'Divergence (Smart Buy)'; // Implied smart buy
114
- }
115
-
116
- result[key] = {
117
- status: status,
118
- smart_flow_pct: sFlow,
119
- dumb_flow_pct: dFlow
120
- };
121
- }
122
- return result;
123
- }
124
-
125
- getResult(fetchedDependencies) {
126
- const smartFlowData = fetchedDependencies['smart-cohort-flow'];
127
- const dumbFlowData = fetchedDependencies['dumb-cohort-flow'];
128
-
129
- const assetResult = this._calculateDivergence(smartFlowData?.assets, dumbFlowData?.assets);
130
- const sectorResult = this._calculateDivergence(smartFlowData?.sectors, dumbFlowData?.sectors);
131
-
132
- return {
133
- assets: assetResult,
134
- sectors: sectorResult
135
- };
136
- }
137
-
138
- reset() {
139
- // No state
140
- }
141
- }
142
-
143
- module.exports = SmartDumbDivergenceIndex;
@@ -1,138 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 5) for smart-dumb divergence v2.
3
- *
4
- * This is a more advanced version that likely uses a different
5
- * cohort definition (e.g., 'expectancy' based) and combines more
6
- * signals.
7
- *
8
- * It *depends* on 'positive_expectancy_cohort_flow' and
9
- * 'negative_expectancy_cohort_flow'.
10
- */
11
- class SmartDumbDivergenceIndexV2 {
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 signalSchema = {
22
- "type": "object",
23
- "properties": {
24
- "status": {
25
- "type": "string",
26
- "enum": ["Capitulation", "Euphoria", "Confirmation (Buy)", "Confirmation (Sell)", "Divergence (Smart Buy)", "Divergence (Smart Sell)", "Neutral"]
27
- },
28
- "smart_flow_pct": { "type": "number" },
29
- "dumb_flow_pct": { "type": "number" }
30
- },
31
- "required": ["status", "smart_flow_pct", "dumb_flow_pct"]
32
- };
33
-
34
- return {
35
- "type": "object",
36
- "description": "Generates divergence signals (V2) by comparing net flow of 'Positive Expectancy' vs. 'Negative Expectancy' cohorts.",
37
- "properties": {
38
- "assets": {
39
- "type": "object",
40
- "description": "Divergence signals per asset.",
41
- "patternProperties": { "^.*$": signalSchema }, // Ticker
42
- "additionalProperties": signalSchema
43
- },
44
- "sectors": {
45
- "type": "object",
46
- "description": "Divergence signals per sector.",
47
- "patternProperties": { "^.*$": signalSchema }, // Sector
48
- "additionalProperties": signalSchema
49
- }
50
- },
51
- "required": ["assets", "sectors"]
52
- };
53
- }
54
-
55
- /**
56
- * Statically declare dependencies.
57
- */
58
- static getDependencies() {
59
- return [
60
- 'positive_expectancy_cohort_flow', // Pass 4
61
- 'negative_expectancy_cohort_flow' // Pass 4
62
- ];
63
- }
64
-
65
- process() {
66
- // No-op
67
- }
68
-
69
- _calculateDivergence(smartFlow, dumbFlow) {
70
- const result = {};
71
- if (!smartFlow || !dumbFlow) {
72
- return result;
73
- }
74
-
75
- const allKeys = new Set([...Object.keys(smartFlow), ...Object.keys(dumbFlow)]);
76
- const THRESHOLD = 1; // Min flow %
77
-
78
- for (const key of allKeys) {
79
- // "Smart" = Positive Expectancy
80
- const sFlow = smartFlow[key]?.net_flow_percentage || 0;
81
- // "Dumb" = Negative Expectancy
82
- const dFlow = dumbFlow[key]?.net_flow_percentage || 0;
83
-
84
- let status = 'Neutral';
85
-
86
- if (sFlow > THRESHOLD && dFlow > THRESHOLD) {
87
- status = 'Confirmation (Buy)';
88
- }
89
- else if (sFlow < -THRESHOLD && dFlow < -THRESHOLD) {
90
- status = 'Confirmation (Sell)';
91
- }
92
- else if (sFlow > THRESHOLD && dFlow < -THRESHOLD) {
93
- status = 'Capitulation';
94
- }
95
- else if (sFlow < -THRESHOLD && dFlow > THRESHOLD) {
96
- status = 'Euphoria';
97
- }
98
- else if (sFlow > THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
99
- status = 'Divergence (Smart Buy)';
100
- }
101
- else if (sFlow < -THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
102
- status = 'Divergence (Smart Sell)';
103
- }
104
- else if (dFlow > THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
105
- status = 'Divergence (Smart Sell)'; // Implied
106
- }
107
- else if (dFlow < -THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
108
- status = 'Divergence (Smart Buy)'; // Implied
109
- }
110
-
111
- result[key] = {
112
- status: status,
113
- smart_flow_pct: sFlow,
114
- dumb_flow_pct: dFlow
115
- };
116
- }
117
- return result;
118
- }
119
-
120
- getResult(fetchedDependencies) {
121
- const smartFlowData = fetchedDependencies['positive_expectancy_cohort_flow'];
122
- const dumbFlowData = fetchedDependencies['negative_expectancy_cohort_flow'];
123
-
124
- const assetResult = this._calculateDivergence(smartFlowData?.assets, dumbFlowData?.assets);
125
- const sectorResult = this._calculateDivergence(smartFlowData?.sectors, dumbFlowData?.sectors);
126
-
127
- return {
128
- assets: assetResult,
129
- sectors: sectorResult
130
- };
131
- }
132
-
133
- reset() {
134
- // No state
135
- }
136
- }
137
-
138
- module.exports = SmartDumbDivergenceIndexV2;
@@ -1,198 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 3) for smart money flow.
3
- *
4
- * This metric calculates the "Net Crowd Flow Percentage" for
5
- * "Smart Money" users, defined as users who were profitable
6
- * in 5 of the last 7 days.
7
- *
8
- * This calculation *depends* on 'user_profitability_tracker'
9
- * to identify the cohort.
10
- */
11
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
12
-
13
- class SmartMoneyFlow {
14
- constructor() {
15
- // This calculation only aggregates by sector
16
- this.sectorData = new Map();
17
- this.mappings = null;
18
- this.smartMoneyUserIds = null;
19
- }
20
-
21
- /**
22
- * Defines the output schema for this calculation.
23
- * @returns {object} JSON Schema object
24
- */
25
- static getSchema() {
26
- return {
27
- "type": "object",
28
- "description": "Calculates net capital flow % (price-adjusted) for the 'Smart Money' cohort (profitable 5 of last 7 days), aggregated by sector.",
29
- "properties": {
30
- "cohort_size": {
31
- "type": "number",
32
- "description": "The number of users identified as being in the Smart Money Cohort."
33
- },
34
- "sectors": {
35
- "type": "object",
36
- "description": "Price-adjusted net flow per sector.",
37
- "patternProperties": {
38
- // Sector
39
- "^.*$": {
40
- "type": "object",
41
- "properties": {
42
- "net_flow_percentage": { "type": "number" },
43
- "total_invested_today": { "type": "number" },
44
- "total_invested_yesterday": { "type": "number" }
45
- },
46
- "required": ["net_flow_percentage", "total_invested_today", "total_invested_yesterday"]
47
- }
48
- },
49
- "additionalProperties": {
50
- "type": "object",
51
- "properties": {
52
- "net_flow_percentage": { "type": "number" },
53
- "total_invested_today": { "type": "number" },
54
- "total_invested_yesterday": { "type": "number" }
55
- }
56
- }
57
- }
58
- },
59
- "required": ["cohort_size", "sectors"]
60
- };
61
- }
62
-
63
- /**
64
- * Statically declare dependencies.
65
- */
66
- static getDependencies() {
67
- return ['user_profitability_tracker'];
68
- }
69
-
70
- _getPortfolioPositions(portfolio) {
71
- return portfolio?.PublicPositions || portfolio?.AggregatedPositions;
72
- }
73
-
74
- _initSector(sector) {
75
- if (!this.sectorData.has(sector)) {
76
- this.sectorData.set(sector, {
77
- total_invested_yesterday: 0,
78
- total_invested_today: 0,
79
- price_change_yesterday: 0,
80
- });
81
- }
82
- }
83
-
84
- /**
85
- * Helper to get the cohort IDs from the dependency.
86
- */
87
- _getSmartMoneyCohort(fetchedDependencies) {
88
- if (this.smartMoneyUserIds) {
89
- return this.smartMoneyUserIds;
90
- }
91
-
92
- const profitabilityData = fetchedDependencies['user_profitability_tracker'];
93
- if (!profitabilityData || !profitabilityData.user_details) {
94
- return new Set();
95
- }
96
-
97
- this.smartMoneyUserIds = new Set();
98
- for (const [userId, data] of Object.entries(profitabilityData.user_details)) {
99
- // Definition: Profitable in 5 of the last 7 days.
100
- if (data.profitable_days_7d >= 5) {
101
- this.smartMoneyUserIds.add(userId);
102
- }
103
- }
104
- return this.smartMoneyUserIds;
105
- }
106
-
107
- process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
108
- const smartMoneyCohort = this._getSmartMoneyCohort(fetchedDependencies);
109
-
110
- // This user is not in the "smart money cohort", skip.
111
- if (!smartMoneyCohort.has(userId)) {
112
- return;
113
- }
114
-
115
- if (!todayPortfolio || !yesterdayPortfolio) {
116
- return;
117
- }
118
-
119
- const yPos = this._getPortfolioPositions(yesterdayPortfolio);
120
- const tPos = this._getPortfolioPositions(todayPortfolio);
121
-
122
- const yPosMap = new Map(yPos?.map(p => [p.InstrumentID, p]) || []);
123
- const tPosMap = new Map(tPos?.map(p => [p.InstrumentID, p]) || []);
124
-
125
- const allInstrumentIds = new Set([...yPosMap.keys(), ...tPosMap.keys()]);
126
-
127
- if (!this.mappings) {
128
- // Context contains the mappings loaded in Pass 1
129
- this.mappings = context.mappings;
130
- }
131
-
132
- for (const instrumentId of allInstrumentIds) {
133
- if (!instrumentId) continue;
134
-
135
- const yP = yPosMap.get(instrumentId);
136
- const tP = tPosMap.get(instrumentId);
137
-
138
- const yInvested = yP?.InvestedAmount || yP?.Amount || 0;
139
- const tInvested = tP?.InvestedAmount || tP?.Amount || 0;
140
-
141
- // Get sector and initialize it
142
- const sector = this.mappings.instrumentToSector[instrumentId] || 'Other';
143
- this._initSector(sector);
144
- const sectorAsset = this.sectorData.get(sector);
145
-
146
- if (yInvested > 0) {
147
- const yPriceChange = (yP?.PipsRate || 0) / (yP?.OpenRate || 1);
148
- sectorAsset.total_invested_yesterday += yInvested;
149
- sectorAsset.price_change_yesterday += yPriceChange * yInvested;
150
- }
151
- if (tInvested > 0) {
152
- sectorAsset.total_invested_today += tInvested;
153
- }
154
- }
155
- }
156
-
157
- async getResult(fetchedDependencies) {
158
- // Ensure mappings are loaded (can be from context or loaded now)
159
- if (!this.mappings) {
160
- this.mappings = await loadInstrumentMappings();
161
- }
162
-
163
- // Ensure cohort is calculated at least once
164
- const smartMoneyCohort = this._getSmartMoneyCohort(fetchedDependencies);
165
-
166
- // Calculate Sector Flow
167
- const sectorResult = {};
168
- for (const [sector, data] of this.sectorData.entries()) {
169
- const { total_invested_yesterday, total_invested_today, price_change_yesterday } = data;
170
-
171
- if (total_invested_yesterday > 0) {
172
- const avg_price_change_pct = price_change_yesterday / total_invested_yesterday;
173
- const price_contribution = total_invested_yesterday * avg_price_change_pct;
174
- const flow_contribution = total_invested_today - (total_invested_yesterday + price_contribution);
175
- const net_flow_percentage = (flow_contribution / total_invested_yesterday) * 100;
176
-
177
- sectorResult[sector] = {
178
- net_flow_percentage: net_flow_percentage,
179
- total_invested_today: total_invested_today,
180
- total_invested_yesterday: total_invested_yesterday
181
- };
182
- }
183
- }
184
-
185
- return {
186
- cohort_size: smartMoneyCohort.size,
187
- sectors: sectorResult
188
- };
189
- }
190
-
191
- reset() {
192
- this.sectorData.clear();
193
- this.mappings = null;
194
- this.smartMoneyUserIds = null;
195
- }
196
- }
197
-
198
- module.exports = SmartMoneyFlow;
@@ -1,102 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 5) for social predictive regime.
3
- *
4
- * This metric answers: "Are we in a 'social-driven' or 'flow-driven'
5
- * market regime?"
6
- *
7
- * It *depends* on 'social_flow_correlation' (Pass 4) to make
8
- * a determination.
9
- */
10
- class SocialPredictiveRegimeState {
11
- constructor() {
12
- // No per-user processing
13
- }
14
-
15
- /**
16
- * Defines the output schema for this calculation.
17
- * @returns {object} JSON Schema object
18
- */
19
- static getSchema() {
20
- return {
21
- "type": "object",
22
- "description": "Determines if the market is in a 'social-driven' or 'flow-driven' regime based on correlation.",
23
- "properties": {
24
- "regime": {
25
- "type": "string",
26
- "enum": ["Social-Driven", "Flow-Driven", "Decoupled", "Unknown"],
27
- "description": "The current market regime."
28
- },
29
- "average_correlation": {
30
- "type": "number",
31
- "description": "The average correlation value across all assets from 'social_flow_correlation'."
32
- },
33
- "correlated_assets_count": {
34
- "type": "number",
35
- "description": "Number of assets with a strong positive correlation."
36
- },
37
- "decoupled_assets_count": {
38
- "type": "number",
39
- "description": "Number of assets with a weak or negative correlation."
40
- }
41
- },
42
- "required": ["regime", "average_correlation", "correlated_assets_count", "decoupled_assets_count"]
43
- };
44
- }
45
-
46
- /**
47
- * Statically declare dependencies.
48
- */
49
- static getDependencies() {
50
- return [
51
- 'social_flow_correlation' // Pass 4
52
- ];
53
- }
54
-
55
- process() {
56
- // No-op
57
- }
58
-
59
- getResult(fetchedDependencies) {
60
- const correlationData = fetchedDependencies['social_flow_correlation'];
61
-
62
- const defaults = {
63
- regime: "Unknown",
64
- average_correlation: 0,
65
- correlated_assets_count: 0,
66
- decoupled_assets_count: 0
67
- };
68
-
69
- if (!correlationData) {
70
- return defaults;
71
- }
72
-
73
- const correlations = Object.values(correlationData).map(d => d.correlation_30d).filter(c => c !== null);
74
- if (correlations.length === 0) {
75
- return defaults;
76
- }
77
-
78
- const avgCorrelation = correlations.reduce((a, b) => a + b, 0) / correlations.length;
79
- const correlatedCount = correlations.filter(c => c > 0.5).length;
80
- const decoupledCount = correlations.filter(c => c <= 0.5).length;
81
-
82
- let regime = "Decoupled";
83
- if (avgCorrelation > 0.5 && correlatedCount > decoupledCount) {
84
- regime = "Social-Driven";
85
- } else if (avgCorrelation < 0.2) {
86
- regime = "Flow-Driven"; // Social is not predictive, flow is
87
- }
88
-
89
- return {
90
- regime: regime,
91
- average_correlation: avgCorrelation,
92
- correlated_assets_count: correlatedCount,
93
- decoupled_assets_count: decoupledCount
94
- };
95
- }
96
-
97
- reset() {
98
- // No state
99
- }
100
- }
101
-
102
- module.exports = SocialPredictiveRegimeState;