aiden-shared-calculations-unified 1.0.86 → 1.0.88
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/calculations/capitulation/asset-volatility-estimator.js +96 -0
- package/calculations/capitulation/retail-capitulation-risk-forecast.js +173 -0
- package/calculations/core/asset-cost-basis-profile.js +127 -0
- package/calculations/core/asset-pnl-status.js +36 -106
- package/calculations/core/asset-position-size.js +40 -91
- package/calculations/core/average-daily-pnl-all-users.js +18 -57
- package/calculations/core/average-daily-pnl-per-sector.js +41 -88
- package/calculations/core/average-daily-pnl-per-stock.js +38 -91
- package/calculations/core/average-daily-position-pnl.js +19 -49
- package/calculations/core/holding-duration-per-asset.js +25 -127
- package/calculations/core/instrument-price-change-1d.js +30 -49
- package/calculations/core/instrument-price-momentum-20d.js +50 -60
- package/calculations/core/long-position-per-stock.js +39 -68
- package/calculations/core/overall-holding-duration.js +16 -87
- package/calculations/core/overall-profitability-ratio.js +11 -40
- package/calculations/core/platform-buy-sell-sentiment.js +41 -124
- package/calculations/core/platform-daily-bought-vs-sold-count.js +41 -99
- package/calculations/core/platform-daily-ownership-delta.js +68 -126
- package/calculations/core/platform-ownership-per-sector.js +45 -96
- package/calculations/core/platform-total-positions-held.js +20 -80
- package/calculations/core/pnl-distribution-per-stock.js +29 -135
- package/calculations/core/price-metrics.js +95 -206
- package/calculations/core/profitability-ratio-per-sector.js +34 -79
- package/calculations/core/profitability-ratio-per-stock.js +32 -88
- package/calculations/core/profitability-skew-per-stock.js +41 -94
- package/calculations/core/profitable-and-unprofitable-status.js +44 -76
- package/calculations/core/sentiment-per-stock.js +24 -77
- package/calculations/core/short-position-per-stock.js +35 -43
- package/calculations/core/social-activity-aggregation.js +26 -49
- package/calculations/core/social-asset-posts-trend.js +38 -94
- package/calculations/core/social-event-correlation.js +26 -93
- package/calculations/core/social-sentiment-aggregation.js +20 -44
- package/calculations/core/social-top-mentioned-words.js +35 -87
- package/calculations/core/social-topic-interest-evolution.js +22 -111
- package/calculations/core/social-topic-sentiment-matrix.js +38 -104
- package/calculations/core/social-word-mentions-trend.js +27 -104
- package/calculations/core/speculator-asset-sentiment.js +31 -72
- package/calculations/core/speculator-danger-zone.js +48 -84
- package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +20 -52
- package/calculations/core/speculator-distance-to-tp-per-leverage.js +23 -53
- package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +20 -50
- package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +23 -50
- package/calculations/core/speculator-leverage-per-asset.js +25 -64
- package/calculations/core/speculator-leverage-per-sector.js +27 -63
- package/calculations/core/speculator-risk-reward-ratio-per-asset.js +24 -53
- package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +55 -68
- package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +54 -71
- package/calculations/core/speculator-stop-loss-per-asset.js +19 -44
- package/calculations/core/speculator-take-profit-per-asset.js +20 -57
- package/calculations/core/speculator-tsl-per-asset.js +17 -56
- package/calculations/core/test..js +0 -0
- package/calculations/core/total-long-figures.js +16 -31
- package/calculations/core/total-long-per-sector.js +39 -61
- package/calculations/core/total-short-figures.js +13 -32
- package/calculations/core/total-short-per-sector.js +39 -61
- package/calculations/core/users-processed.js +11 -46
- package/calculations/gauss/cohort-capital-flow.js +54 -173
- package/calculations/gauss/cohort-definer.js +77 -163
- package/calculations/gauss/daily-dna-filter.js +29 -83
- package/calculations/gauss/gauss-divergence-signal.js +22 -109
- package/calculations/gem/cohort-momentum-state.js +27 -72
- package/calculations/gem/cohort-skill-definition.js +36 -52
- package/calculations/gem/platform-conviction-divergence.js +18 -60
- package/calculations/gem/quant-skill-alpha-signal.js +25 -98
- package/calculations/gem/skilled-cohort-flow.js +67 -175
- package/calculations/gem/skilled-unskilled-divergence.js +18 -73
- package/calculations/gem/unskilled-cohort-flow.js +64 -172
- package/calculations/ghost-book/cost-basis-density.js +79 -0
- package/calculations/ghost-book/liquidity-vacuum.js +52 -0
- package/calculations/ghost-book/retail-gamma-exposure.js +86 -0
- package/calculations/helix/helix-contrarian-signal.js +20 -114
- package/calculations/helix/herd-consensus-score.js +42 -124
- package/calculations/helix/winner-loser-flow.js +36 -118
- package/calculations/predicative-alpha/cognitive-dissonance.js +113 -0
- package/calculations/predicative-alpha/diamond-hand-fracture.js +90 -0
- package/calculations/predicative-alpha/mimetic-latency.js +124 -0
- package/calculations/pyro/risk-appetite-index.js +33 -74
- package/calculations/pyro/squeeze-potential.js +30 -87
- package/calculations/pyro/volatility-signal.js +33 -78
- package/package.json +1 -1
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Calculation (Pass 1) for short positions per stock.
|
|
3
|
-
*
|
|
4
|
-
* - Logic updated to check for *both* Speculator schema (pos.IsBuy === false)
|
|
5
|
-
* and Normal schema (pos.Direction === 'Sell') to match schema.md.
|
|
3
|
+
* REFACTORED: Tracks exposure weight (percentage).
|
|
6
4
|
*/
|
|
7
|
-
|
|
8
5
|
class ShortPositionPerStock {
|
|
9
6
|
constructor() {
|
|
10
|
-
this.
|
|
7
|
+
this.stockData = new Map();
|
|
11
8
|
this.tickerMap = null;
|
|
12
9
|
}
|
|
13
10
|
|
|
@@ -21,70 +18,65 @@ class ShortPositionPerStock {
|
|
|
21
18
|
};
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
static getDependencies() {
|
|
25
|
-
return [];
|
|
26
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
27
22
|
|
|
28
23
|
static getSchema() {
|
|
29
|
-
|
|
24
|
+
const schema = {
|
|
30
25
|
"type": "object",
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"
|
|
26
|
+
"properties": {
|
|
27
|
+
"short_count": { "type": "number" },
|
|
28
|
+
"total_short_exposure_weight": { "type": "number", "description": "Total allocation % for short positions." }
|
|
34
29
|
},
|
|
35
|
-
"
|
|
30
|
+
"required": ["short_count", "total_short_exposure_weight"]
|
|
36
31
|
};
|
|
32
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
if (!this.
|
|
41
|
-
this.
|
|
35
|
+
_initStock(instId) {
|
|
36
|
+
if (!this.stockData.has(instId)) {
|
|
37
|
+
this.stockData.set(instId, { count: 0, weight: 0 });
|
|
42
38
|
}
|
|
43
39
|
}
|
|
44
40
|
|
|
45
|
-
process(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
process(context) {
|
|
42
|
+
const { extract } = context.math;
|
|
43
|
+
const { mappings, user } = context;
|
|
44
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
49
45
|
|
|
50
|
-
const positions =
|
|
51
|
-
if (!positions || !Array.isArray(positions)) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
46
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
54
47
|
|
|
55
48
|
for (const pos of positions) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const isShort = (pos.IsBuy === false) || (pos.Direction === 'Sell');
|
|
49
|
+
const direction = extract.getDirection(pos);
|
|
50
|
+
if (direction !== 'Sell') continue; // Only Shorts
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const instrumentId = pos.InstrumentID;
|
|
63
|
-
if (!instrumentId) continue;
|
|
52
|
+
const instId = extract.getInstrumentId(pos);
|
|
53
|
+
if (!instId) continue;
|
|
64
54
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
const weight = extract.getPositionWeight(pos, user.type);
|
|
56
|
+
|
|
57
|
+
this._initStock(instId);
|
|
58
|
+
const data = this.stockData.get(instId);
|
|
59
|
+
data.count++;
|
|
60
|
+
data.weight += weight;
|
|
68
61
|
}
|
|
69
62
|
}
|
|
70
63
|
|
|
71
64
|
async getResult() {
|
|
72
|
-
if (!this.tickerMap) {
|
|
73
|
-
return {};
|
|
74
|
-
}
|
|
75
|
-
|
|
65
|
+
if (!this.tickerMap) return {};
|
|
76
66
|
const result = {};
|
|
77
|
-
for (const [
|
|
78
|
-
const ticker = this.tickerMap[
|
|
79
|
-
result[ticker] =
|
|
67
|
+
for (const [instId, data] of this.stockData.entries()) {
|
|
68
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
69
|
+
result[ticker] = {
|
|
70
|
+
short_count: data.count,
|
|
71
|
+
total_short_exposure_weight: data.weight
|
|
72
|
+
};
|
|
80
73
|
}
|
|
81
74
|
return result;
|
|
82
75
|
}
|
|
83
76
|
|
|
84
77
|
reset() {
|
|
85
|
-
this.
|
|
78
|
+
this.stockData.clear();
|
|
86
79
|
this.tickerMap = null;
|
|
87
80
|
}
|
|
88
81
|
}
|
|
89
|
-
|
|
90
82
|
module.exports = ShortPositionPerStock;
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Calculation (Pass 2 - Meta) for social activity.
|
|
3
|
-
*
|
|
4
|
-
* 1. Changed 'process' signature to 5-arg 'meta' standard.
|
|
5
|
-
* 2. Changed data access to read 'dateStr.social' (Arg 1, the metaPayload).
|
|
6
|
-
* 3. Changed 'socialDoc.posts' to 'socialDoc' (which IS the map of posts).
|
|
7
|
-
* 4. Changed 'post.comments' to 'post.commentCount'.
|
|
8
|
-
* 5. Changed 'post.likes' to 'post.likeCount'.
|
|
3
|
+
* REFACTORED: Uses context.social.today.
|
|
9
4
|
*/
|
|
10
5
|
class SocialActivityAggregation {
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
this.result = {};
|
|
14
|
-
}
|
|
6
|
+
constructor() { this.result = {}; }
|
|
15
7
|
|
|
16
8
|
static getMetadata() {
|
|
17
9
|
return {
|
|
@@ -23,60 +15,45 @@ class SocialActivityAggregation {
|
|
|
23
15
|
};
|
|
24
16
|
}
|
|
25
17
|
|
|
26
|
-
static getDependencies() {
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
18
|
+
static getDependencies() { return []; }
|
|
29
19
|
|
|
30
20
|
static getSchema() {
|
|
31
21
|
return {
|
|
32
22
|
"type": "object",
|
|
33
|
-
"description": "Aggregates the total social activity for the day.",
|
|
34
23
|
"properties": {
|
|
35
24
|
"total_posts": { "type": "number" },
|
|
36
|
-
"
|
|
37
|
-
"total_likes": { "type": "number" }
|
|
25
|
+
"unique_authors": { "type": "number" },
|
|
26
|
+
"total_likes": { "type": "number" },
|
|
27
|
+
"total_comments": { "type": "number" }
|
|
38
28
|
},
|
|
39
|
-
"required": ["total_posts", "
|
|
29
|
+
"required": ["total_posts", "unique_authors", "total_likes", "total_comments"]
|
|
40
30
|
};
|
|
41
31
|
}
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let totalComments = 0;
|
|
57
|
-
let totalLikes = 0;
|
|
58
|
-
|
|
59
|
-
for (const post of postsArray) {
|
|
60
|
-
// Use schema.md fields
|
|
61
|
-
totalComments += post.commentCount || 0;
|
|
62
|
-
totalLikes += post.likeCount || 0;
|
|
33
|
+
process(context) {
|
|
34
|
+
// Access social data from context
|
|
35
|
+
const socialPosts = context.social?.today || {};
|
|
36
|
+
|
|
37
|
+
let posts = 0, likes = 0, comments = 0;
|
|
38
|
+
const authors = new Set();
|
|
39
|
+
|
|
40
|
+
for (const postId in socialPosts) {
|
|
41
|
+
const post = socialPosts[postId];
|
|
42
|
+
posts++;
|
|
43
|
+
likes += (post.likeCount || 0);
|
|
44
|
+
comments += (post.commentCount || 0);
|
|
45
|
+
if (post.postOwnerId) authors.add(post.postOwnerId);
|
|
63
46
|
}
|
|
64
47
|
|
|
65
48
|
this.result = {
|
|
66
|
-
total_posts:
|
|
67
|
-
|
|
68
|
-
total_likes:
|
|
49
|
+
total_posts: posts,
|
|
50
|
+
unique_authors: authors.size,
|
|
51
|
+
total_likes: likes,
|
|
52
|
+
total_comments: comments
|
|
69
53
|
};
|
|
70
54
|
}
|
|
71
|
-
// --- END FIX ---
|
|
72
|
-
|
|
73
|
-
async getResult(fetchedDependencies) {
|
|
74
|
-
return this.result;
|
|
75
|
-
}
|
|
76
55
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
56
|
+
async getResult() { return this.result; }
|
|
57
|
+
reset() { this.result = {}; }
|
|
80
58
|
}
|
|
81
|
-
|
|
82
59
|
module.exports = SocialActivityAggregation;
|
|
@@ -1,125 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 2 - Meta) for
|
|
3
|
-
*
|
|
4
|
-
* 1. Changed 'process' signature to 5-arg 'meta' standard.
|
|
5
|
-
* 2. Changed data access to read 'dateStr.social' (Arg 1).
|
|
6
|
-
* 3. Changed 'socialDoc.posts' (Object) to 'socialDoc' (Object).
|
|
7
|
-
* 4. Changed 'post.timestamp' to 'post.createdAt'.
|
|
8
|
-
* 5. Changed 'post.instrumentIds' to 'post.tickers'.
|
|
9
|
-
* 6. Removed 'instrumentToTicker' mapping as 'post.tickers' is already tickers.
|
|
2
|
+
* @fileoverview Calculation (Pass 2 - Meta) for asset mention trends.
|
|
3
|
+
* REFACTORED: Aggregates post counts per ticker.
|
|
10
4
|
*/
|
|
11
|
-
|
|
12
5
|
class SocialAssetPostsTrend {
|
|
13
|
-
|
|
14
|
-
constructor() {
|
|
15
|
-
this.result = {};
|
|
16
|
-
}
|
|
6
|
+
constructor() { this.result = {}; }
|
|
17
7
|
|
|
18
8
|
static getMetadata() {
|
|
19
9
|
return {
|
|
20
10
|
type: 'meta',
|
|
21
11
|
rootDataDependencies: ['social'],
|
|
22
|
-
isHistorical:
|
|
12
|
+
isHistorical: false,
|
|
23
13
|
userType: 'n/a',
|
|
24
14
|
category: 'core_social'
|
|
25
15
|
};
|
|
26
16
|
}
|
|
27
17
|
|
|
28
|
-
static getDependencies() {
|
|
29
|
-
return [];
|
|
30
|
-
}
|
|
18
|
+
static getDependencies() { return []; }
|
|
31
19
|
|
|
32
20
|
static getSchema() {
|
|
33
21
|
const tickerSchema = {
|
|
34
22
|
"type": "object",
|
|
35
23
|
"properties": {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"trend_ratio": { "type": ["number", "null"] }
|
|
24
|
+
"post_count": { "type": "number" },
|
|
25
|
+
"sentiment_score": { "type": "number" }
|
|
39
26
|
},
|
|
40
|
-
"required": ["
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
"type": "object",
|
|
45
|
-
"description": "Calculates the 1-day vs 7-day percentage of total posts for each asset.",
|
|
46
|
-
"patternProperties": { "^.*$": tickerSchema },
|
|
47
|
-
"additionalProperties": tickerSchema
|
|
27
|
+
"required": ["post_count", "sentiment_score"]
|
|
48
28
|
};
|
|
29
|
+
return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
|
|
49
30
|
}
|
|
50
31
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
for (const post of Object.values(socialDoc)) {
|
|
74
|
-
const postDate = new Date(post.createdAt); // Use 'createdAt'
|
|
75
|
-
const is1d = postDate.toISOString().slice(0, 10) === todayDateStr;
|
|
76
|
-
const is7d = postDate >= sevenDaysAgo;
|
|
77
|
-
|
|
78
|
-
if (!is7d) continue;
|
|
79
|
-
|
|
80
|
-
// Use 'tickers' array directly from schema
|
|
81
|
-
for (const ticker of (post.tickers || [])) {
|
|
82
|
-
if (!ticker) continue;
|
|
83
|
-
|
|
84
|
-
if (is7d) {
|
|
85
|
-
counts7d.set(ticker, (counts7d.get(ticker) || 0) + 1);
|
|
86
|
-
total7d++;
|
|
87
|
-
}
|
|
88
|
-
if (is1d) {
|
|
89
|
-
counts1d.set(ticker, (counts1d.get(ticker) || 0) + 1);
|
|
90
|
-
total1d++;
|
|
91
|
-
}
|
|
32
|
+
process(context) {
|
|
33
|
+
const socialPosts = context.social?.today || {};
|
|
34
|
+
const assetStats = new Map(); // { ticker: { count: 0, sentimentSum: 0 } }
|
|
35
|
+
|
|
36
|
+
for (const postId in socialPosts) {
|
|
37
|
+
const post = socialPosts[postId];
|
|
38
|
+
if (!post.tickers || !Array.isArray(post.tickers)) continue;
|
|
39
|
+
|
|
40
|
+
// Convert sentiment to numeric: Bullish=1, Bearish=-1, Neutral=0
|
|
41
|
+
let sentVal = 0;
|
|
42
|
+
const sentiment = (post.sentiment?.overallSentiment || '').toLowerCase();
|
|
43
|
+
if (sentiment === 'bullish') sentVal = 1;
|
|
44
|
+
else if (sentiment === 'bearish') sentVal = -1;
|
|
45
|
+
|
|
46
|
+
for (const ticker of post.tickers) {
|
|
47
|
+
const t = ticker.toUpperCase();
|
|
48
|
+
if (!assetStats.has(t)) assetStats.set(t, { count: 0, sentimentSum: 0 });
|
|
49
|
+
|
|
50
|
+
const stats = assetStats.get(t);
|
|
51
|
+
stats.count++;
|
|
52
|
+
stats.sentimentSum += sentVal;
|
|
92
53
|
}
|
|
93
54
|
}
|
|
94
55
|
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const c7 = counts7d.get(ticker) || 0;
|
|
101
|
-
|
|
102
|
-
const pct1d = (total1d > 0) ? (c1 / total1d) * 100 : 0;
|
|
103
|
-
const pct7d = (total7d > 0) ? (c7 / total7d) * 100 : 0;
|
|
104
|
-
|
|
105
|
-
result[ticker] = {
|
|
106
|
-
posts_1d_pct: pct1d,
|
|
107
|
-
posts_7d_pct: pct7d,
|
|
108
|
-
trend_ratio: (pct7d > 0) ? (pct1d / pct7d) : null
|
|
56
|
+
const output = {};
|
|
57
|
+
for (const [ticker, stats] of assetStats.entries()) {
|
|
58
|
+
output[ticker] = {
|
|
59
|
+
post_count: stats.count,
|
|
60
|
+
sentiment_score: stats.count > 0 ? stats.sentimentSum / stats.count : 0
|
|
109
61
|
};
|
|
110
62
|
}
|
|
111
|
-
|
|
112
|
-
this.result = result;
|
|
113
|
-
}
|
|
114
|
-
// --- END FIX ---
|
|
115
|
-
|
|
116
|
-
async getResult(fetchedDependencies) {
|
|
117
|
-
return this.result;
|
|
63
|
+
this.result = output;
|
|
118
64
|
}
|
|
119
65
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
66
|
+
async getResult() { return this.result; }
|
|
67
|
+
reset() { this.result = {}; }
|
|
123
68
|
}
|
|
124
|
-
|
|
125
69
|
module.exports = SocialAssetPostsTrend;
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 2 - Meta) for social event
|
|
3
|
-
*
|
|
4
|
-
* 1. Changed 'process' signature to 5-arg 'meta' standard.
|
|
5
|
-
* 2. Changed data access to read 'dateStr.social' (Arg 1).
|
|
6
|
-
* 3. Changed 'socialDoc.posts' to 'socialDoc' (Object).
|
|
7
|
-
* 4. **Rewrote logic** to infer events from 'post.pollData' and
|
|
8
|
-
* 'post.sentiment.topics' as 'post.event' does not exist in schema.
|
|
2
|
+
* @fileoverview Calculation (Pass 2 - Meta) for social event extraction.
|
|
3
|
+
* REFACTORED: Analyzes events from social posts.
|
|
9
4
|
*/
|
|
10
5
|
class SocialEventCorrelation {
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
this.result = {};
|
|
14
|
-
}
|
|
6
|
+
constructor() { this.result = {}; }
|
|
15
7
|
|
|
16
8
|
static getMetadata() {
|
|
17
9
|
return {
|
|
@@ -23,103 +15,44 @@ class SocialEventCorrelation {
|
|
|
23
15
|
};
|
|
24
16
|
}
|
|
25
17
|
|
|
26
|
-
static getDependencies() {
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
18
|
+
static getDependencies() { return []; }
|
|
29
19
|
|
|
30
20
|
static getSchema() {
|
|
31
|
-
const eventSchema = {
|
|
32
|
-
"type": "object",
|
|
33
|
-
"properties": {
|
|
34
|
-
"topic_distribution": { "type": "object" },
|
|
35
|
-
"sentiment_distribution": { "type": "object" },
|
|
36
|
-
"post_count": { "type": "number" }
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
21
|
return {
|
|
41
22
|
"type": "object",
|
|
42
|
-
"description": "
|
|
43
|
-
"
|
|
44
|
-
"poll": eventSchema,
|
|
45
|
-
"earnings": eventSchema, // Inferred from topics
|
|
46
|
-
"launch": eventSchema // Inferred from topics
|
|
47
|
-
}
|
|
23
|
+
"description": "Map of events detected in social stream.",
|
|
24
|
+
"patternProperties": { "^.*$": { "type": "number", "description": "Count of mentions" } }
|
|
48
25
|
};
|
|
49
26
|
}
|
|
50
27
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
topic_distribution: {},
|
|
55
|
-
sentiment_distribution: {},
|
|
56
|
-
post_count: 0
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// --- THIS IS THE FIX ---
|
|
62
|
-
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
63
|
-
const result = {};
|
|
64
|
-
|
|
65
|
-
// Get social data from Arg 1 (metaPayload)
|
|
66
|
-
const socialDoc = dateStr.social; // This IS the map of posts
|
|
67
|
-
|
|
68
|
-
if (!socialDoc || typeof socialDoc !== 'object') {
|
|
69
|
-
this.result = {};
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
for (const post of Object.values(socialDoc)) {
|
|
28
|
+
process(context) {
|
|
29
|
+
const socialPosts = context.social?.today || {};
|
|
30
|
+
const events = new Map();
|
|
74
31
|
|
|
75
|
-
|
|
32
|
+
for (const postId in socialPosts) {
|
|
33
|
+
const post = socialPosts[postId];
|
|
34
|
+
// Extract 'topics' from the sentiment analysis result
|
|
76
35
|
const topics = post.sentiment?.topics || [];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const detectedEvents = new Set();
|
|
80
|
-
|
|
81
|
-
// Event 1: Check for Polls
|
|
82
|
-
if (post.pollData) {
|
|
83
|
-
detectedEvents.add('poll');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Event 2: Check for Topics as Events
|
|
87
|
-
if (topics.includes('Earnings')) {
|
|
88
|
-
detectedEvents.add('earnings');
|
|
89
|
-
}
|
|
90
|
-
// Add any other topic-based events you want to track
|
|
91
|
-
// if (topics.includes('Launch')) {
|
|
92
|
-
// detectedEvents.add('launch');
|
|
93
|
-
// }
|
|
94
|
-
|
|
95
|
-
if (detectedEvents.size === 0) continue;
|
|
96
|
-
|
|
97
|
-
for (const eventName of detectedEvents) {
|
|
98
|
-
this._initEvent(result, eventName);
|
|
99
|
-
const eventData = result[eventName];
|
|
100
|
-
eventData.post_count++;
|
|
101
|
-
|
|
102
|
-
// Aggregate topics
|
|
36
|
+
|
|
37
|
+
if (Array.isArray(topics)) {
|
|
103
38
|
for (const topic of topics) {
|
|
104
|
-
|
|
39
|
+
// Basic cleanup
|
|
40
|
+
const cleanTopic = topic.trim().toUpperCase();
|
|
41
|
+
if (cleanTopic.length > 2) {
|
|
42
|
+
events.set(cleanTopic, (events.get(cleanTopic) || 0) + 1);
|
|
43
|
+
}
|
|
105
44
|
}
|
|
106
|
-
|
|
107
|
-
// Aggregate sentiment
|
|
108
|
-
eventData.sentiment_distribution[sentiment] = (eventData.sentiment_distribution[sentiment] || 0) + 1;
|
|
109
45
|
}
|
|
110
46
|
}
|
|
47
|
+
// Filter for significant events (mentioned > 1 times)
|
|
48
|
+
const significant = [...events.entries()]
|
|
49
|
+
.filter(([_, count]) => count > 1)
|
|
50
|
+
.sort((a, b) => b[1] - a[1]);
|
|
111
51
|
|
|
112
|
-
this.result =
|
|
52
|
+
this.result = Object.fromEntries(significant);
|
|
113
53
|
}
|
|
114
|
-
// --- END FIX ---
|
|
115
54
|
|
|
116
|
-
async getResult(
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
reset() {
|
|
121
|
-
this.result = {};
|
|
122
|
-
}
|
|
55
|
+
async getResult() { return this.result; }
|
|
56
|
+
reset() { this.result = {}; }
|
|
123
57
|
}
|
|
124
|
-
|
|
125
58
|
module.exports = SocialEventCorrelation;
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 2 - Meta) for
|
|
3
|
-
*
|
|
4
|
-
* 1. Changed 'process' signature to 5-arg 'meta' standard.
|
|
5
|
-
* 2. Changed data access to read 'dateStr.social' (Arg 1).
|
|
6
|
-
* 3. Changed 'socialDoc.posts' (Object) to 'socialDoc' (Object).
|
|
7
|
-
* 4. Changed 'post.sentiment.label' to 'post.sentiment.overallSentiment'.
|
|
2
|
+
* @fileoverview Calculation (Pass 2 - Meta) for sentiment aggregation.
|
|
3
|
+
* REFACTORED: Uses context.social.today and sentiment analysis results.
|
|
8
4
|
*/
|
|
9
5
|
class SocialSentimentAggregation {
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
this.result = {};
|
|
13
|
-
}
|
|
6
|
+
constructor() { this.result = {}; }
|
|
14
7
|
|
|
15
8
|
static getMetadata() {
|
|
16
9
|
return {
|
|
@@ -22,70 +15,53 @@ class SocialSentimentAggregation {
|
|
|
22
15
|
};
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
static getDependencies() {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
18
|
+
static getDependencies() { return []; }
|
|
28
19
|
|
|
29
20
|
static getSchema() {
|
|
30
21
|
return {
|
|
31
22
|
"type": "object",
|
|
32
|
-
"description": "Aggregates the overall social sentiment for the day.",
|
|
33
23
|
"properties": {
|
|
34
24
|
"bullish": { "type": "number" },
|
|
35
25
|
"bearish": { "type": "number" },
|
|
36
26
|
"neutral": { "type": "number" },
|
|
37
27
|
"total_posts": { "type": "number" },
|
|
38
|
-
"sentiment_score": { "type": "number"
|
|
28
|
+
"sentiment_score": { "type": "number" }
|
|
39
29
|
},
|
|
40
30
|
"required": ["bullish", "bearish", "neutral", "total_posts", "sentiment_score"]
|
|
41
31
|
};
|
|
42
32
|
}
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
process(context) {
|
|
35
|
+
const socialPosts = context.social?.today || {};
|
|
46
36
|
const counts = { bullish: 0, bearish: 0, neutral: 0 };
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
// Get social data from Arg 1 (metaPayload)
|
|
50
|
-
const socialDoc = dateStr.social; // This IS the map of posts
|
|
51
|
-
|
|
52
|
-
if (!socialDoc || typeof socialDoc !== 'object') {
|
|
53
|
-
this.result = { bullish: 0, bearish: 0, neutral: 0, total_posts: 0, sentiment_score: 0 };
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
37
|
+
let total = 0;
|
|
56
38
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
for (const post of postsArray) {
|
|
61
|
-
// Use 'overallSentiment' and convert to lowercase
|
|
39
|
+
for (const postId in socialPosts) {
|
|
40
|
+
const post = socialPosts[postId];
|
|
62
41
|
const sentiment = (post.sentiment?.overallSentiment || 'neutral').toLowerCase();
|
|
42
|
+
|
|
63
43
|
if (counts.hasOwnProperty(sentiment)) {
|
|
64
44
|
counts[sentiment]++;
|
|
45
|
+
total++;
|
|
46
|
+
} else {
|
|
47
|
+
counts.neutral++; // Default fallback
|
|
48
|
+
total++;
|
|
65
49
|
}
|
|
66
50
|
}
|
|
67
51
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
: 0;
|
|
52
|
+
// Score: (Bullish - Bearish) / Total (-1 to 1 range)
|
|
53
|
+
const score = total > 0 ? (counts.bullish - counts.bearish) / total : 0;
|
|
71
54
|
|
|
72
55
|
this.result = {
|
|
73
56
|
bullish: counts.bullish,
|
|
74
57
|
bearish: counts.bearish,
|
|
75
58
|
neutral: counts.neutral,
|
|
76
|
-
total_posts:
|
|
59
|
+
total_posts: total,
|
|
77
60
|
sentiment_score: score
|
|
78
61
|
};
|
|
79
62
|
}
|
|
80
|
-
// --- END FIX ---
|
|
81
|
-
|
|
82
|
-
async getResult(fetchedDependencies) {
|
|
83
|
-
return this.result;
|
|
84
|
-
}
|
|
85
63
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
64
|
+
async getResult() { return this.result; }
|
|
65
|
+
reset() { this.result = {}; }
|
|
89
66
|
}
|
|
90
|
-
|
|
91
67
|
module.exports = SocialSentimentAggregation;
|