aiden-shared-calculations-unified 1.0.63 → 1.0.65
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 +1 -1
- package/calculations/activity/historical/activity_by_pnl_status.js +33 -0
- package/calculations/activity/historical/daily_asset_activity.js +42 -0
- package/calculations/activity/historical/daily_user_activity_tracker.js +37 -0
- package/calculations/activity/historical/speculator_adjustment_activity.js +26 -0
- package/calculations/asset_metrics/asset_position_size.js +36 -0
- package/calculations/backtests/strategy-performance.js +41 -0
- package/calculations/behavioural/historical/asset_crowd_flow.js +124 -127
- package/calculations/behavioural/historical/drawdown_response.js +113 -35
- package/calculations/behavioural/historical/dumb-cohort-flow.js +191 -171
- package/calculations/behavioural/historical/gain_response.js +113 -34
- package/calculations/behavioural/historical/historical_performance_aggregator.js +63 -48
- package/calculations/behavioural/historical/in_loss_asset_crowd_flow.js +159 -63
- package/calculations/behavioural/historical/in_profit_asset_crowd_flow.js +159 -64
- package/calculations/behavioural/historical/paper_vs_diamond_hands.js +86 -19
- package/calculations/behavioural/historical/position_count_pnl.js +91 -39
- package/calculations/behavioural/historical/smart-cohort-flow.js +192 -172
- package/calculations/behavioural/historical/smart_money_flow.js +160 -151
- package/calculations/capital_flow/historical/crowd-cash-flow-proxy.js +95 -89
- package/calculations/capital_flow/historical/deposit_withdrawal_percentage.js +88 -81
- package/calculations/capital_flow/historical/new_allocation_percentage.js +75 -26
- package/calculations/capital_flow/historical/reallocation_increase_percentage.js +73 -32
- package/calculations/insights/daily_buy_sell_sentiment_count.js +47 -32
- package/calculations/insights/daily_total_positions_held.js +28 -24
- package/calculations/insights/historical/daily_bought_vs_sold_count.js +101 -36
- package/calculations/insights/historical/daily_ownership_delta.js +95 -32
- package/calculations/meta/capital_deployment_strategy.js +78 -110
- package/calculations/meta/capital_liquidation_performance.js +114 -111
- package/calculations/meta/cash-flow-deployment.js +114 -107
- package/calculations/meta/cash-flow-liquidation.js +114 -107
- package/calculations/meta/crowd_sharpe_ratio_proxy.js +94 -54
- package/calculations/meta/negative_expectancy_cohort_flow.js +185 -177
- package/calculations/meta/positive_expectancy_cohort_flow.js +186 -181
- package/calculations/meta/profit_cohort_divergence.js +83 -59
- package/calculations/meta/shark_attack_signal.js +91 -39
- package/calculations/meta/smart-dumb-divergence-index.js +114 -98
- package/calculations/meta/smart_dumb_divergence_index_v2.js +109 -98
- package/calculations/meta/social-predictive-regime-state.js +76 -155
- package/calculations/meta/social-topic-driver-index.js +74 -127
- package/calculations/meta/user_expectancy_score.js +83 -31
- package/calculations/pnl/asset_pnl_status.js +120 -31
- package/calculations/pnl/average_daily_pnl_all_users.js +42 -27
- package/calculations/pnl/average_daily_pnl_per_sector.js +84 -26
- package/calculations/pnl/average_daily_pnl_per_stock.js +71 -29
- package/calculations/pnl/average_daily_position_pnl.js +49 -21
- package/calculations/pnl/historical/profitability_migration.js +81 -35
- package/calculations/pnl/historical/user_profitability_tracker.js +107 -104
- package/calculations/pnl/pnl_distribution_per_stock.js +65 -45
- package/calculations/pnl/profitability_ratio_per_stock.js +78 -21
- package/calculations/pnl/profitability_skew_per_stock.js +86 -31
- package/calculations/pnl/profitable_and_unprofitable_status.js +45 -45
- package/calculations/sanity/users_processed.js +24 -1
- package/calculations/sectors/historical/diversification_pnl.js +104 -42
- package/calculations/sectors/historical/sector_rotation.js +94 -45
- package/calculations/sectors/total_long_per_sector.js +55 -20
- package/calculations/sectors/total_short_per_sector.js +55 -20
- package/calculations/sentiment/historical/crowd_conviction_score.js +233 -53
- package/calculations/short_and_long_stats/long_position_per_stock.js +50 -14
- package/calculations/short_and_long_stats/sentiment_per_stock.js +76 -19
- package/calculations/short_and_long_stats/short_position_per_stock.js +50 -13
- package/calculations/short_and_long_stats/total_long_figures.js +34 -13
- package/calculations/short_and_long_stats/total_short_figures.js +34 -14
- package/calculations/socialPosts/social-asset-posts-trend.js +96 -29
- package/calculations/socialPosts/social-top-mentioned-words.js +95 -74
- package/calculations/socialPosts/social-topic-interest-evolution.js +92 -29
- package/calculations/socialPosts/social-topic-sentiment-matrix.js +70 -78
- package/calculations/socialPosts/social-word-mentions-trend.js +96 -38
- package/calculations/socialPosts/social_activity_aggregation.js +106 -77
- package/calculations/socialPosts/social_sentiment_aggregation.js +115 -86
- package/calculations/speculators/distance_to_stop_loss_per_leverage.js +82 -43
- package/calculations/speculators/distance_to_tp_per_leverage.js +81 -42
- package/calculations/speculators/entry_distance_to_sl_per_leverage.js +80 -44
- package/calculations/speculators/entry_distance_to_tp_per_leverage.js +81 -44
- package/calculations/speculators/historical/risk_appetite_change.js +89 -32
- package/calculations/speculators/historical/tsl_effectiveness.js +57 -47
- package/calculations/speculators/holding_duration_per_asset.js +83 -23
- package/calculations/speculators/leverage_per_asset.js +68 -19
- package/calculations/speculators/leverage_per_sector.js +86 -25
- package/calculations/speculators/risk_reward_ratio_per_asset.js +82 -28
- package/calculations/speculators/speculator_asset_sentiment.js +100 -48
- package/calculations/speculators/speculator_danger_zone.js +101 -33
- package/calculations/speculators/stop_loss_distance_by_sector_short_long_breakdown.js +93 -66
- package/calculations/speculators/stop_loss_distance_by_ticker_short_long_breakdown.js +94 -47
- package/calculations/speculators/stop_loss_per_asset.js +94 -26
- package/calculations/speculators/take_profit_per_asset.js +95 -27
- package/calculations/speculators/tsl_per_asset.js +77 -23
- package/package.json +1 -1
- package/utils/price_data_provider.js +142 -142
- package/utils/sector_mapping_provider.js +74 -74
|
@@ -1,102 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
* This
|
|
2
|
+
* @fileoverview Social Calculation (Pass 1)
|
|
3
|
+
*
|
|
4
|
+
* This metric answers: "What are the top 50 most mentioned
|
|
5
|
+
* words in social media posts today, and what is their
|
|
6
|
+
* trend since yesterday?"
|
|
7
|
+
*
|
|
8
|
+
* This is a stateful calculation.
|
|
5
9
|
*/
|
|
6
|
-
|
|
7
10
|
class SocialTopMentionedWords {
|
|
8
11
|
constructor() {
|
|
9
|
-
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// A small, representative list of stop words.
|
|
14
|
-
// In a real implementation, this list would be much larger.
|
|
15
|
-
this.stopWords = new Set([
|
|
16
|
-
'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
17
|
-
'have', 'has', 'had', 'do', 'does', 'did', 'but', 'if', 'or', 'and',
|
|
18
|
-
'of', 'at', 'by', 'for', 'with', 'about', 'to', 'from', 'in', 'on',
|
|
19
|
-
'it', 'i', 'me', 'my', 'you', 'your', 'he', 'she', 'we', 'they', 'them',
|
|
20
|
-
'this', 'that', 'these', 'those', 'what', 'which', 'who', 'whom',
|
|
21
|
-
'not', 'will', 'just', 'so', 'up', 'out', 'no', 'yes', 'all', 'any'
|
|
22
|
-
]);
|
|
12
|
+
// { [word]: { count: 0, history: [] } }
|
|
13
|
+
this.wordCounts = new Map();
|
|
14
|
+
// A set of common words to ignore
|
|
15
|
+
this.stopWords = new Set(['the', 'a', 'is', 'in', 'to', 'for', 'of', 'and', 'it', 'on', 'with', 'that', 'this', 'buy', 'sell']);
|
|
23
16
|
}
|
|
24
17
|
|
|
25
18
|
/**
|
|
26
|
-
*
|
|
27
|
-
* @
|
|
28
|
-
* @param {object} targetCountMap - The object to store word counts.
|
|
19
|
+
* Defines the output schema for this calculation.
|
|
20
|
+
* @returns {object} JSON Schema object
|
|
29
21
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
static getSchema() {
|
|
23
|
+
const wordSchema = {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"word": { "type": "string" },
|
|
27
|
+
"count": { "type": "number" },
|
|
28
|
+
"trend_30d_pct": { "type": "number" },
|
|
29
|
+
"history_30d": { "type": "array", "items": { "type": "number" } }
|
|
30
|
+
},
|
|
31
|
+
"required": ["word", "count", "trend_30d_pct", "history_30d"]
|
|
32
|
+
};
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
34
|
+
return {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"description": "Top 50 most mentioned words (non-stop words) with their 30-day trend.",
|
|
37
|
+
"items": wordSchema
|
|
38
|
+
};
|
|
47
39
|
}
|
|
48
40
|
|
|
49
41
|
/**
|
|
50
|
-
* @param {
|
|
51
|
-
* @param {object}
|
|
42
|
+
* @param {string} dateStr - Today's date.
|
|
43
|
+
* @param {object} dependencies - db, logger.
|
|
44
|
+
* @param {object} config - Computation config.
|
|
45
|
+
* @param {object} fetchedDependencies - (UNUSED) In-memory results.
|
|
52
46
|
*/
|
|
53
|
-
async process(
|
|
47
|
+
async process(dateStr, dependencies, config, fetchedDependencies) {
|
|
48
|
+
const { db, logger, calculationUtils } = dependencies;
|
|
49
|
+
|
|
50
|
+
// 1. Load this metric's history from yesterday
|
|
51
|
+
// Note: History is stored by word
|
|
52
|
+
const yHistoryData = await calculationUtils.loadYesterdayData('social-top-mentioned-words-history'); // Fictional helper
|
|
54
53
|
|
|
55
|
-
if (
|
|
56
|
-
|
|
54
|
+
if (yHistoryData) {
|
|
55
|
+
for (const [word, data] of Object.entries(yHistoryData)) {
|
|
56
|
+
this.wordCounts.set(word, {
|
|
57
|
+
count: 0,
|
|
58
|
+
history: data.history_30d || []
|
|
59
|
+
});
|
|
60
|
+
}
|
|
57
61
|
}
|
|
58
|
-
this.processed = true;
|
|
59
|
-
|
|
60
|
-
// Process today's posts
|
|
61
|
-
this._countWords(todaySocialPostInsights, this.todayWordCounts);
|
|
62
62
|
|
|
63
|
-
// Process
|
|
64
|
-
|
|
65
|
-
}
|
|
63
|
+
// 2. Process today's raw social data
|
|
64
|
+
const todaySocialPosts = await calculationUtils.loadSocialData(dateStr);
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
for (const post of todaySocialPosts) {
|
|
67
|
+
const text = (post.content || '').toLowerCase().replace(/[^a-z\s]/g, '');
|
|
68
|
+
const words = text.split(/\s+/);
|
|
69
|
+
|
|
70
|
+
for (const word of words) {
|
|
71
|
+
if (word.length > 2 && !this.stopWords.has(word)) {
|
|
72
|
+
if (!this.wordCounts.has(word)) {
|
|
73
|
+
this.wordCounts.set(word, { count: 0, history: [] });
|
|
74
|
+
}
|
|
75
|
+
this.wordCounts.get(word).count++;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
70
78
|
}
|
|
79
|
+
}
|
|
71
80
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
async getResult() {
|
|
82
|
+
const allWords = [];
|
|
83
|
+
|
|
84
|
+
for (const [word, data] of this.wordCounts.entries()) {
|
|
85
|
+
const history = data.history;
|
|
86
|
+
const todayCount = data.count;
|
|
87
|
+
|
|
88
|
+
// Don't include words that weren't mentioned today
|
|
89
|
+
if (todayCount === 0) continue;
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
const yesterdayCount = this.yesterdayWordCounts[term] || 0;
|
|
80
|
-
const trend = count - yesterdayCount;
|
|
81
|
-
const percent = (count / totalTodayWords) * 100;
|
|
91
|
+
const newHistory = [todayCount, ...history].slice(0, 30);
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
let trend_30d_pct = 0;
|
|
94
|
+
if (history.length > 0) {
|
|
95
|
+
const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
|
|
96
|
+
if (avg_30d > 0) {
|
|
97
|
+
trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
90
100
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
allWords.push({
|
|
102
|
+
word: word,
|
|
103
|
+
count: todayCount,
|
|
104
|
+
trend_30d_pct: trend_30d_pct,
|
|
105
|
+
history_30d: newHistory
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Sort by today's count and take top 50
|
|
110
|
+
allWords.sort((a, b) => b.count - a.count);
|
|
111
|
+
|
|
112
|
+
// The *full* result (for history) should be saved to a different
|
|
113
|
+
// doc ('social-top-mentioned-words-history') by the computation system.
|
|
114
|
+
|
|
115
|
+
// Return only the top 50 for the API.
|
|
116
|
+
return allWords.slice(0, 50);
|
|
94
117
|
}
|
|
95
118
|
|
|
96
119
|
reset() {
|
|
97
|
-
this.
|
|
98
|
-
this.yesterdayWordCounts = {};
|
|
99
|
-
this.processed = false;
|
|
120
|
+
this.wordCounts.clear();
|
|
100
121
|
}
|
|
101
122
|
}
|
|
102
123
|
|
|
@@ -1,53 +1,116 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
* This
|
|
2
|
+
* @fileoverview Social Calculation (Pass 1)
|
|
3
|
+
*
|
|
4
|
+
* This metric answers: "What is the daily count of
|
|
5
|
+
* posts for each topic extracted from social media?"
|
|
6
|
+
*
|
|
7
|
+
* This is a stateful calculation.
|
|
5
8
|
*/
|
|
6
|
-
|
|
7
9
|
class SocialTopicInterestEvolution {
|
|
8
10
|
constructor() {
|
|
9
|
-
|
|
10
|
-
this.
|
|
11
|
+
// { [topic]: { count: 0, history: [] } }
|
|
12
|
+
this.topicData = new Map();
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
|
|
17
|
-
* @param {object} yesterdaySocialPostInsights - Not used.
|
|
16
|
+
* Defines the output schema for this calculation.
|
|
17
|
+
* @returns {object} JSON Schema object
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
static getSchema() {
|
|
20
|
+
const topicSchema = {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"description": "Daily and trended post count for a specific topic.",
|
|
23
|
+
"properties": {
|
|
24
|
+
"count": {
|
|
25
|
+
"type": "number",
|
|
26
|
+
"description": "Today's raw post count for this topic."
|
|
27
|
+
},
|
|
28
|
+
"trend_30d_pct": {
|
|
29
|
+
"type": "number",
|
|
30
|
+
"description": "Percentage change from the 30-day average post count."
|
|
31
|
+
},
|
|
32
|
+
"history_30d": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"description": "30-day history of post counts (today first).",
|
|
35
|
+
"items": { "type": "number" }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"required": ["count", "trend_30d_pct", "history_30d"]
|
|
39
|
+
};
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
return {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"description": "Tracks the daily post count and 30-day trend for each extracted topic.",
|
|
44
|
+
"patternProperties": {
|
|
45
|
+
"^.*$": topicSchema // Topic name
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": topicSchema
|
|
48
|
+
};
|
|
49
|
+
}
|
|
27
50
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
/**
|
|
52
|
+
* @param {string} dateStr - Today's date.
|
|
53
|
+
* @param {object} dependencies - db, logger.
|
|
54
|
+
* @param {object} config - Computation config.
|
|
55
|
+
* @param {object} fetchedDependencies - (UNUSED) In-memory results.
|
|
56
|
+
*/
|
|
57
|
+
async process(dateStr, dependencies, config, fetchedDependencies) {
|
|
58
|
+
const { db, logger, calculationUtils } = dependencies;
|
|
59
|
+
|
|
60
|
+
// 1. Load this metric's history from yesterday
|
|
61
|
+
const yHistoryData = await calculationUtils.loadYesterdayData('social-topic-interest-evolution');
|
|
32
62
|
|
|
63
|
+
if (yHistoryData) {
|
|
64
|
+
for (const [topic, data] of Object.entries(yHistoryData)) {
|
|
65
|
+
this.topicData.set(topic, {
|
|
66
|
+
count: 0,
|
|
67
|
+
history: data.history_30d || []
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 2. Process today's raw social data
|
|
73
|
+
const todaySocialPosts = await calculationUtils.loadSocialData(dateStr);
|
|
74
|
+
|
|
75
|
+
for (const post of todaySocialPosts) {
|
|
76
|
+
// Assume topics are extracted by the social loader
|
|
77
|
+
const topics = post.topics || [];
|
|
33
78
|
for (const topic of topics) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (normalizedTopic) {
|
|
37
|
-
this.topicCounts[normalizedTopic] = (this.topicCounts[normalizedTopic] || 0) + 1;
|
|
79
|
+
if (!this.topicData.has(topic)) {
|
|
80
|
+
this.topicData.set(topic, { count: 0, history: [] });
|
|
38
81
|
}
|
|
82
|
+
this.topicData.get(topic).count++;
|
|
39
83
|
}
|
|
40
84
|
}
|
|
41
85
|
}
|
|
42
86
|
|
|
43
87
|
async getResult() {
|
|
44
|
-
|
|
45
|
-
|
|
88
|
+
const result = {};
|
|
89
|
+
for (const [topic, data] of this.topicData.entries()) {
|
|
90
|
+
const history = data.history;
|
|
91
|
+
const todayCount = data.count;
|
|
92
|
+
|
|
93
|
+
const newHistory = [todayCount, ...history].slice(0, 30);
|
|
94
|
+
|
|
95
|
+
let trend_30d_pct = 0;
|
|
96
|
+
if (history.length > 0) {
|
|
97
|
+
const avg_30d = history.reduce((a, b) => a + b, 0) / history.length;
|
|
98
|
+
if (avg_30d > 0) {
|
|
99
|
+
trend_30d_pct = ((todayCount - avg_30d) / avg_30d) * 100;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
result[topic] = {
|
|
104
|
+
count: todayCount,
|
|
105
|
+
trend_30d_pct: trend_30d_pct,
|
|
106
|
+
history_30d: newHistory
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
46
110
|
}
|
|
47
111
|
|
|
48
112
|
reset() {
|
|
49
|
-
this.
|
|
50
|
-
this.processed = false;
|
|
113
|
+
this.topicData.clear();
|
|
51
114
|
}
|
|
52
115
|
}
|
|
53
116
|
|
|
@@ -1,104 +1,96 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
* Aggregates all social post data for the day into a detailed matrix.
|
|
4
|
-
* For each ticker, it maps each discussed topic to its aggregated
|
|
5
|
-
* sentiment (Bullish/Bearish/Neutral) and a "Conviction Score"
|
|
6
|
-
* derived from likes and comments.
|
|
2
|
+
* @fileoverview Social Calculation (Pass 1)
|
|
7
3
|
*
|
|
8
|
-
* This
|
|
4
|
+
* This metric answers: "What is the sentiment breakdown
|
|
5
|
+
* (bullish, bearish) for each topic?"
|
|
6
|
+
*
|
|
7
|
+
* Creates a matrix of Topic <-> Sentiment.
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
9
|
class SocialTopicSentimentMatrix {
|
|
12
10
|
constructor() {
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
this.matrix = {};
|
|
16
|
-
|
|
17
|
-
// Flag to ensure this runs only once per day
|
|
18
|
-
this.processed = false;
|
|
11
|
+
// { [topic]: { bullish: 0, bearish: 0, neutral: 0 } }
|
|
12
|
+
this.matrix = new Map();
|
|
19
13
|
}
|
|
20
14
|
|
|
21
15
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @
|
|
24
|
-
* @param {null} userId - Not used.
|
|
25
|
-
* @param {object} context - Shared context.
|
|
26
|
-
* @param {object} todayInsights - Not used.
|
|
27
|
-
* @param {object} yesterdayInsights - Not used.
|
|
28
|
-
* @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
|
|
29
|
-
* @param {object} yesterdaySocialPostInsights - Not used.
|
|
16
|
+
* Defines the output schema for this calculation.
|
|
17
|
+
* @returns {object} JSON Schema object
|
|
30
18
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
19
|
+
static getSchema() {
|
|
20
|
+
const sentimentSchema = {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"bullish": { "type": "number" },
|
|
24
|
+
"bearish": { "type": "number" },
|
|
25
|
+
"neutral": { "type": "number" },
|
|
26
|
+
"net_sentiment": { "type": "number" }
|
|
27
|
+
},
|
|
28
|
+
"required": ["bullish", "bearish", "neutral", "net_sentiment"]
|
|
29
|
+
};
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
return {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"description": "Calculates the sentiment breakdown (bullish, bearish, neutral) for each topic.",
|
|
34
|
+
"patternProperties": {
|
|
35
|
+
"^.*$": sentimentSchema // Topic name
|
|
36
|
+
},
|
|
37
|
+
"additionalProperties": sentimentSchema
|
|
38
|
+
};
|
|
39
|
+
}
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
_initTopic(topic) {
|
|
42
|
+
if (!this.matrix.has(topic)) {
|
|
43
|
+
this.matrix.set(topic, {
|
|
44
|
+
bullish: 0,
|
|
45
|
+
bearish: 0,
|
|
46
|
+
neutral: 0
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
/**
|
|
52
|
+
* @param {string} dateStr - Today's date.
|
|
53
|
+
* @param {object} dependencies - db, logger.
|
|
54
|
+
* @param {object} config - Computation config.
|
|
55
|
+
* @param {object} fetchedDependencies - (UNUSED) In-memory results.
|
|
56
|
+
*/
|
|
57
|
+
async process(dateStr, dependencies, config, fetchedDependencies) {
|
|
58
|
+
const { calculationUtils } = dependencies;
|
|
59
|
+
const todaySocialPosts = await calculationUtils.loadSocialData(dateStr);
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
for (const post of todaySocialPosts) {
|
|
62
|
+
const topics = post.topics || [];
|
|
63
|
+
const sentiment = post.sentiment || 'neutral'; // 'bullish', 'bearish', 'neutral'
|
|
64
|
+
|
|
65
|
+
for (const topic of topics) {
|
|
66
|
+
this._initTopic(topic);
|
|
67
|
+
const data = this.matrix.get(topic);
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
// Initialize topic map
|
|
67
|
-
if (!this.matrix[ticker][topicLower]) {
|
|
68
|
-
this.matrix[ticker][topicLower] = {
|
|
69
|
-
bullishPosts: 0,
|
|
70
|
-
bearishPosts: 0,
|
|
71
|
-
neutralPosts: 0,
|
|
72
|
-
totalPosts: 0,
|
|
73
|
-
convictionScore: 0
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Aggregate data into the bucket
|
|
78
|
-
const bucket = this.matrix[ticker][topicLower];
|
|
79
|
-
bucket.totalPosts++;
|
|
80
|
-
bucket.convictionScore += convictionScore;
|
|
81
|
-
|
|
82
|
-
if (sentiment === 'Bullish') {
|
|
83
|
-
bucket.bullishPosts++;
|
|
84
|
-
} else if (sentiment === 'Bearish') {
|
|
85
|
-
bucket.bearishPosts++;
|
|
86
|
-
} else {
|
|
87
|
-
bucket.neutralPosts++;
|
|
88
|
-
}
|
|
69
|
+
if (sentiment === 'bullish') {
|
|
70
|
+
data.bullish++;
|
|
71
|
+
} else if (sentiment === 'bearish') {
|
|
72
|
+
data.bearish++;
|
|
73
|
+
} else {
|
|
74
|
+
data.neutral++;
|
|
89
75
|
}
|
|
90
76
|
}
|
|
91
77
|
}
|
|
92
78
|
}
|
|
93
79
|
|
|
94
80
|
async getResult() {
|
|
95
|
-
|
|
96
|
-
|
|
81
|
+
const result = {};
|
|
82
|
+
for (const [topic, data] of this.matrix.entries()) {
|
|
83
|
+
result[topic] = {
|
|
84
|
+
...data,
|
|
85
|
+
// Net Sentiment = (Bullish - Bearish)
|
|
86
|
+
net_sentiment: data.bullish - data.bearish
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
reset() {
|
|
100
|
-
this.matrix
|
|
101
|
-
this.processed = false;
|
|
93
|
+
this.matrix.clear();
|
|
102
94
|
}
|
|
103
95
|
}
|
|
104
96
|
|