aiden-shared-calculations-unified 1.0.34 → 1.0.36
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.
- package/README.MD +77 -77
- package/calculations/activity/historical/activity_by_pnl_status.js +85 -85
- package/calculations/activity/historical/daily_asset_activity.js +85 -85
- package/calculations/activity/historical/daily_user_activity_tracker.js +144 -144
- package/calculations/activity/historical/speculator_adjustment_activity.js +76 -76
- package/calculations/asset_metrics/asset_position_size.js +57 -57
- package/calculations/backtests/strategy-performance.js +229 -245
- package/calculations/behavioural/historical/asset_crowd_flow.js +165 -170
- package/calculations/behavioural/historical/drawdown_response.js +58 -58
- package/calculations/behavioural/historical/dumb-cohort-flow.js +249 -249
- package/calculations/behavioural/historical/gain_response.js +57 -57
- package/calculations/behavioural/historical/in_loss_asset_crowd_flow.js +98 -98
- package/calculations/behavioural/historical/in_profit_asset_crowd_flow.js +99 -99
- package/calculations/behavioural/historical/paper_vs_diamond_hands.js +39 -39
- package/calculations/behavioural/historical/position_count_pnl.js +67 -67
- package/calculations/behavioural/historical/smart-cohort-flow.js +250 -250
- package/calculations/behavioural/historical/smart_money_flow.js +165 -165
- package/calculations/behavioural/historical/user-investment-profile.js +412 -412
- package/calculations/capital_flow/historical/crowd-cash-flow-proxy.js +121 -121
- package/calculations/capital_flow/historical/deposit_withdrawal_percentage.js +117 -117
- package/calculations/capital_flow/historical/new_allocation_percentage.js +49 -49
- package/calculations/insights/daily_bought_vs_sold_count.js +55 -55
- package/calculations/insights/daily_buy_sell_sentiment_count.js +49 -49
- package/calculations/insights/daily_ownership_delta.js +55 -55
- package/calculations/insights/daily_total_positions_held.js +39 -39
- package/calculations/meta/capital_deployment_strategy.js +129 -137
- package/calculations/meta/capital_liquidation_performance.js +121 -163
- package/calculations/meta/capital_vintage_performance.js +121 -158
- package/calculations/meta/cash-flow-deployment.js +110 -124
- package/calculations/meta/cash-flow-liquidation.js +126 -142
- package/calculations/meta/crowd_sharpe_ratio_proxy.js +83 -91
- package/calculations/meta/profit_cohort_divergence.js +77 -91
- package/calculations/meta/smart-dumb-divergence-index.js +116 -138
- package/calculations/meta/social_flow_correlation.js +99 -125
- package/calculations/pnl/asset_pnl_status.js +46 -46
- package/calculations/pnl/historical/profitability_migration.js +57 -57
- package/calculations/pnl/historical/user_profitability_tracker.js +117 -117
- package/calculations/pnl/profitable_and_unprofitable_status.js +64 -64
- package/calculations/sectors/historical/diversification_pnl.js +76 -76
- package/calculations/sectors/historical/sector_rotation.js +67 -67
- package/calculations/sentiment/historical/crowd_conviction_score.js +80 -80
- package/calculations/socialPosts/social-asset-posts-trend.js +52 -52
- package/calculations/socialPosts/social-top-mentioned-words.js +102 -102
- package/calculations/socialPosts/social-topic-interest-evolution.js +53 -53
- package/calculations/socialPosts/social-word-mentions-trend.js +62 -62
- package/calculations/socialPosts/social_activity_aggregation.js +103 -103
- package/calculations/socialPosts/social_event_correlation.js +121 -121
- package/calculations/socialPosts/social_sentiment_aggregation.js +114 -114
- package/calculations/speculators/historical/risk_appetite_change.js +54 -54
- package/calculations/speculators/historical/tsl_effectiveness.js +74 -74
- package/index.js +33 -33
- package/package.json +32 -32
- package/utils/firestore_utils.js +76 -76
- package/utils/price_data_provider.js +142 -142
- package/utils/sector_mapping_provider.js +74 -74
- package/calculations/capital_flow/historical/reallocation_increase_percentage.js +0 -63
- package/calculations/speculators/stop_loss_distance_by_sector_short_long_breakdown.js +0 -91
- package/calculations/speculators/stop_loss_distance_by_ticker_short_long_breakdown.js +0 -73
|
@@ -1,139 +1,117 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Meta-calculation (Pass 3) that correlates the asset/sector flow
|
|
3
|
-
* of the "Smart Cohort" vs. the "Dumb Cohort" to find divergence signals.
|
|
4
|
-
*
|
|
5
|
-
* This identifies:
|
|
6
|
-
* 1. "Capitulation": Smart cohort is buying what the dumb cohort is panic-selling.
|
|
7
|
-
* 2. "Euphoria": Smart cohort is selling what the dumb cohort is FOMO-buying.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
class SmartDumbDivergenceIndex {
|
|
11
|
-
constructor() {
|
|
12
|
-
// Minimum net flow (as a percentage) to be considered a signal
|
|
13
|
-
this.FLOW_THRESHOLD = 0.005; // Formerly 0.5
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
|
|
18
|
-
* @param {object} dependencies The shared dependencies (db, logger).
|
|
19
|
-
* @param {object} config The computation system configuration.
|
|
20
|
-
* @
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (sFlow > 0 && dFlow < 0) {
|
|
118
|
-
status = 'Capitulation';
|
|
119
|
-
} else if (sFlow < 0 && dFlow > 0) {
|
|
120
|
-
status = 'Euphoria';
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (status !== 'No_Divergence') {
|
|
124
|
-
results.sectors[sector] = {
|
|
125
|
-
status: status,
|
|
126
|
-
smart_cohort_flow_usd: sFlow,
|
|
127
|
-
dumb_cohort_flow_usd: dFlow
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return results;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async getResult() { return null; }
|
|
136
|
-
reset() {}
|
|
137
|
-
}
|
|
138
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Meta-calculation (Pass 3) that correlates the asset/sector flow
|
|
3
|
+
* of the "Smart Cohort" vs. the "Dumb Cohort" to find divergence signals.
|
|
4
|
+
*
|
|
5
|
+
* This identifies:
|
|
6
|
+
* 1. "Capitulation": Smart cohort is buying what the dumb cohort is panic-selling.
|
|
7
|
+
* 2. "Euphoria": Smart cohort is selling what the dumb cohort is FOMO-buying.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
class SmartDumbDivergenceIndex {
|
|
11
|
+
constructor() {
|
|
12
|
+
// Minimum net flow (as a percentage) to be considered a signal
|
|
13
|
+
this.FLOW_THRESHOLD = 0.005; // Formerly 0.5
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
|
|
18
|
+
* @param {object} dependencies The shared dependencies (db, logger).
|
|
19
|
+
* @param {object} config The computation system configuration.
|
|
20
|
+
* @param {object} computedDependencies In-memory results from previous passes.
|
|
21
|
+
* @returns {Promise<object|null>} The analysis result or null.
|
|
22
|
+
*/
|
|
23
|
+
async process(dateStr, dependencies, config, computedDependencies) {
|
|
24
|
+
const { logger } = dependencies;
|
|
25
|
+
|
|
26
|
+
// 1. Get dependencies from in-memory cache
|
|
27
|
+
const smartData = computedDependencies['smart-cohort-flow'];
|
|
28
|
+
const dumbData = computedDependencies['dumb-cohort-flow'];
|
|
29
|
+
|
|
30
|
+
// 2. Handle missing dependencies
|
|
31
|
+
if (!smartData || !dumbData) {
|
|
32
|
+
logger.log('WARN', `[SmartDumbDivergence] Missing cohort flow data dependency for ${dateStr}. Skipping.`);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const results = {
|
|
37
|
+
assets: {},
|
|
38
|
+
sectors: {}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const smartAssetFlow = smartData.asset_flow;
|
|
42
|
+
const dumbAssetFlow = dumbData.asset_flow;
|
|
43
|
+
const smartSectorFlow = smartData.sector_rotation;
|
|
44
|
+
const dumbSectorFlow = dumbData.sector_rotation;
|
|
45
|
+
|
|
46
|
+
if (!smartAssetFlow || !dumbAssetFlow || !smartSectorFlow || !dumbSectorFlow) {
|
|
47
|
+
logger.log('WARN', `[SmartDumbDivergence] Dependency data for ${dateStr} is incomplete (missing asset_flow or sector_rotation). Skipping.`);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 3. Correlate Assets
|
|
52
|
+
const allTickers = new Set([...Object.keys(smartAssetFlow), ...Object.keys(dumbAssetFlow)]);
|
|
53
|
+
for (const ticker of allTickers) {
|
|
54
|
+
const sFlow = smartAssetFlow[ticker]?.net_crowd_flow_pct || 0;
|
|
55
|
+
const dFlow = dumbAssetFlow[ticker]?.net_crowd_flow_pct || 0;
|
|
56
|
+
|
|
57
|
+
const smartBuys = sFlow >= this.FLOW_THRESHOLD;
|
|
58
|
+
const smartSells = sFlow <= -this.FLOW_THRESHOLD;
|
|
59
|
+
const dumbBuys = dFlow >= this.FLOW_THRESHOLD;
|
|
60
|
+
const dumbSells = dFlow <= -this.FLOW_THRESHOLD;
|
|
61
|
+
|
|
62
|
+
let status = 'No_Divergence';
|
|
63
|
+
let detail = 'Cohorts are aligned or flow is insignificant.';
|
|
64
|
+
|
|
65
|
+
if (smartBuys && dumbSells) {
|
|
66
|
+
status = 'Capitulation';
|
|
67
|
+
detail = 'Smart cohort is buying the dip from the panic-selling dumb cohort.';
|
|
68
|
+
} else if (smartSells && dumbBuys) {
|
|
69
|
+
status = 'Euphoria';
|
|
70
|
+
detail = 'Smart cohort is selling into the FOMO-buying dumb cohort.';
|
|
71
|
+
} else if (smartBuys && dumbBuys) {
|
|
72
|
+
status = 'Aligned_Buy';
|
|
73
|
+
} else if (smartSells && dumbSells) {
|
|
74
|
+
status = 'Aligned_Sell';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (status !== 'No_Divergence') {
|
|
78
|
+
results.assets[ticker] = {
|
|
79
|
+
status: status,
|
|
80
|
+
detail: detail,
|
|
81
|
+
smart_cohort_flow_pct: sFlow,
|
|
82
|
+
dumb_cohort_flow_pct: dFlow
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 4. Correlate Sectors
|
|
88
|
+
const allSectors = new Set([...Object.keys(smartSectorFlow), ...Object.keys(dumbSectorFlow)]);
|
|
89
|
+
for (const sector of allSectors) {
|
|
90
|
+
const sFlow = smartSectorFlow[sector] || 0;
|
|
91
|
+
const dFlow = dumbSectorFlow[sector] || 0;
|
|
92
|
+
|
|
93
|
+
let status = 'No_Divergence';
|
|
94
|
+
|
|
95
|
+
if (sFlow > 0 && dFlow < 0) {
|
|
96
|
+
status = 'Capitulation';
|
|
97
|
+
} else if (sFlow < 0 && dFlow > 0) {
|
|
98
|
+
status = 'Euphoria';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (status !== 'No_Divergence') {
|
|
102
|
+
results.sectors[sector] = {
|
|
103
|
+
status: status,
|
|
104
|
+
smart_cohort_flow_usd: sFlow,
|
|
105
|
+
dumb_cohort_flow_usd: dFlow
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return results;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getResult() { return null; }
|
|
114
|
+
reset() {}
|
|
115
|
+
}
|
|
116
|
+
|
|
139
117
|
module.exports = SmartDumbDivergenceIndex;
|
|
@@ -1,126 +1,100 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Meta-calculation (Pass 3) to correlate daily social sentiment with
|
|
3
|
-
* the actual crowd asset flow. It identifies divergences between what the crowd
|
|
4
|
-
* says and what they do.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
class SocialFlowCorrelation {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* @
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
};
|
|
101
|
-
} else if (sentiment <= this.bearishSentimentThreshold && flow <= this.negativeFlowThreshold) {
|
|
102
|
-
// Crowd is bearish and is selling
|
|
103
|
-
correlationResults[ticker] = {
|
|
104
|
-
status: 'High Conviction Sell',
|
|
105
|
-
sentiment_ratio: sentiment,
|
|
106
|
-
net_crowd_flow_pct: flow
|
|
107
|
-
};
|
|
108
|
-
} else {
|
|
109
|
-
// No strong signal or divergence
|
|
110
|
-
correlationResults[ticker] = {
|
|
111
|
-
status: 'No Clear Signal',
|
|
112
|
-
sentiment_ratio: sentiment,
|
|
113
|
-
net_crowd_flow_pct: flow
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return correlationResults;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Must exist for the meta-computation runner
|
|
122
|
-
async getResult() { return null; }
|
|
123
|
-
reset() {}
|
|
124
|
-
}
|
|
125
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Meta-calculation (Pass 3) to correlate daily social sentiment with
|
|
3
|
+
* the actual crowd asset flow. It identifies divergences between what the crowd
|
|
4
|
+
* says and what they do.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class SocialFlowCorrelation {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.bullishSentimentThreshold = 70.0;
|
|
10
|
+
this.bearishSentimentThreshold = 30.0;
|
|
11
|
+
this.positiveFlowThreshold = 0.005;
|
|
12
|
+
this.negativeFlowThreshold = -0.005;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
|
|
17
|
+
* @param {object} dependencies The shared dependencies (db, logger).
|
|
18
|
+
* @param {object} config The computation system configuration.
|
|
19
|
+
* @param {object} computedDependencies In-memory results from previous passes.
|
|
20
|
+
* @returns {Promise<object|null>} The analysis result or null.
|
|
21
|
+
*/
|
|
22
|
+
async process(dateStr, dependencies, config, computedDependencies) {
|
|
23
|
+
const { logger } = dependencies;
|
|
24
|
+
|
|
25
|
+
// 1. Get dependencies from in-memory cache
|
|
26
|
+
const socialData = computedDependencies['social-sentiment-aggregation'];
|
|
27
|
+
const flowData = computedDependencies['asset-crowd-flow'];
|
|
28
|
+
|
|
29
|
+
// 2. Handle missing dependencies
|
|
30
|
+
if (!socialData || !flowData) {
|
|
31
|
+
logger.log('WARN', `[SocialFlowCorrelation] Missing computed dependency data for ${dateStr}. Skipping.`);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 3. If data exists, perform the correlation
|
|
36
|
+
const sentimentMap = socialData.tickerSentiment || {};
|
|
37
|
+
const correlationResults = {};
|
|
38
|
+
|
|
39
|
+
// Use all tickers from the flow data as the primary loop
|
|
40
|
+
for (const ticker in flowData) {
|
|
41
|
+
if (!flowData[ticker] || typeof flowData[ticker].net_crowd_flow_pct === 'undefined') {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const flow = flowData[ticker].net_crowd_flow_pct;
|
|
46
|
+
const sentiment = sentimentMap[ticker]?.sentimentRatio;
|
|
47
|
+
|
|
48
|
+
if (typeof sentiment === 'undefined') {
|
|
49
|
+
correlationResults[ticker] = {
|
|
50
|
+
status: 'no_social_sentiment',
|
|
51
|
+
net_crowd_flow_pct: flow
|
|
52
|
+
};
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// --- The "Jaw-Drop" Logic ---
|
|
57
|
+
if (sentiment >= this.bullishSentimentThreshold && flow <= this.negativeFlowThreshold) {
|
|
58
|
+
correlationResults[ticker] = {
|
|
59
|
+
status: 'Bullish Divergence',
|
|
60
|
+
detail: 'Crowd is publicly bullish but is net-selling the asset.',
|
|
61
|
+
sentiment_ratio: sentiment,
|
|
62
|
+
net_crowd_flow_pct: flow
|
|
63
|
+
};
|
|
64
|
+
} else if (sentiment <= this.bearishSentimentThreshold && flow >= this.positiveFlowThreshold) {
|
|
65
|
+
correlationResults[ticker] = {
|
|
66
|
+
status: 'Bearish Divergence',
|
|
67
|
+
detail: 'Crowd is publicly bearish but is net-buying the asset.',
|
|
68
|
+
sentiment_ratio: sentiment,
|
|
69
|
+
net_crowd_flow_pct: flow
|
|
70
|
+
};
|
|
71
|
+
} else if (sentiment >= this.bullishSentimentThreshold && flow >= this.positiveFlowThreshold) {
|
|
72
|
+
correlationResults[ticker] = {
|
|
73
|
+
status: 'High Conviction Buy',
|
|
74
|
+
sentiment_ratio: sentiment,
|
|
75
|
+
net_crowd_flow_pct: flow
|
|
76
|
+
};
|
|
77
|
+
} else if (sentiment <= this.bearishSentimentThreshold && flow <= this.negativeFlowThreshold) {
|
|
78
|
+
correlationResults[ticker] = {
|
|
79
|
+
status: 'High Conviction Sell',
|
|
80
|
+
sentiment_ratio: sentiment,
|
|
81
|
+
net_crowd_flow_pct: flow
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
correlationResults[ticker] = {
|
|
85
|
+
status: 'No Clear Signal',
|
|
86
|
+
sentiment_ratio: sentiment,
|
|
87
|
+
net_crowd_flow_pct: flow
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return correlationResults;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Must exist for the meta-computation runner
|
|
96
|
+
async getResult() { return null; }
|
|
97
|
+
reset() {}
|
|
98
|
+
}
|
|
99
|
+
|
|
126
100
|
module.exports = SocialFlowCorrelation;
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Counts the number of users in profit vs. loss for each asset (from our sample).
|
|
3
|
-
* This data is used for extrapolation with the ground truth owner count.
|
|
4
|
-
*/
|
|
5
|
-
class AssetPnlStatus {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.assets = {};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
_initAsset(ticker) {
|
|
11
|
-
if (!this.assets[ticker]) {
|
|
12
|
-
this.assets[ticker] = { profit_count: 0, loss_count: 0 };
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
process(portfolioData, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights) {
|
|
17
|
-
const { instrumentMappings } = context;
|
|
18
|
-
const processedTickers = new Set(); // Ensure one user is only counted once per ticker
|
|
19
|
-
|
|
20
|
-
// FIX: Use the correct portfolio position properties
|
|
21
|
-
const positions = portfolioData.AggregatedPositions || portfolioData.PublicPositions;
|
|
22
|
-
if (!positions || !Array.isArray(positions)) return;
|
|
23
|
-
|
|
24
|
-
for (const position of positions) {
|
|
25
|
-
// FIX: Use the correct PascalCase InstrumentID
|
|
26
|
-
const ticker = instrumentMappings[position.InstrumentID];
|
|
27
|
-
if (!ticker || processedTickers.has(ticker)) continue;
|
|
28
|
-
|
|
29
|
-
this._initAsset(ticker);
|
|
30
|
-
|
|
31
|
-
// FIX: Use the correct PascalCase NetProfit
|
|
32
|
-
if (position.NetProfit > 0) {
|
|
33
|
-
this.assets[ticker].profit_count++;
|
|
34
|
-
} else {
|
|
35
|
-
this.assets[ticker].loss_count++;
|
|
36
|
-
}
|
|
37
|
-
processedTickers.add(ticker);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
getResult() {
|
|
42
|
-
// Returns raw counts for frontend extrapolation
|
|
43
|
-
return this.assets;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Counts the number of users in profit vs. loss for each asset (from our sample).
|
|
3
|
+
* This data is used for extrapolation with the ground truth owner count.
|
|
4
|
+
*/
|
|
5
|
+
class AssetPnlStatus {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.assets = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
_initAsset(ticker) {
|
|
11
|
+
if (!this.assets[ticker]) {
|
|
12
|
+
this.assets[ticker] = { profit_count: 0, loss_count: 0 };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
process(portfolioData, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights) {
|
|
17
|
+
const { instrumentMappings } = context;
|
|
18
|
+
const processedTickers = new Set(); // Ensure one user is only counted once per ticker
|
|
19
|
+
|
|
20
|
+
// FIX: Use the correct portfolio position properties
|
|
21
|
+
const positions = portfolioData.AggregatedPositions || portfolioData.PublicPositions;
|
|
22
|
+
if (!positions || !Array.isArray(positions)) return;
|
|
23
|
+
|
|
24
|
+
for (const position of positions) {
|
|
25
|
+
// FIX: Use the correct PascalCase InstrumentID
|
|
26
|
+
const ticker = instrumentMappings[position.InstrumentID];
|
|
27
|
+
if (!ticker || processedTickers.has(ticker)) continue;
|
|
28
|
+
|
|
29
|
+
this._initAsset(ticker);
|
|
30
|
+
|
|
31
|
+
// FIX: Use the correct PascalCase NetProfit
|
|
32
|
+
if (position.NetProfit > 0) {
|
|
33
|
+
this.assets[ticker].profit_count++;
|
|
34
|
+
} else {
|
|
35
|
+
this.assets[ticker].loss_count++;
|
|
36
|
+
}
|
|
37
|
+
processedTickers.add(ticker);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getResult() {
|
|
42
|
+
// Returns raw counts for frontend extrapolation
|
|
43
|
+
return this.assets;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
47
|
module.exports = AssetPnlStatus;
|