aiden-shared-calculations-unified 1.0.83 → 1.0.86
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/core/asset-pnl-status.js +122 -104
- package/calculations/core/asset-position-size.js +110 -73
- package/calculations/core/average-daily-pnl-all-users.js +17 -3
- package/calculations/core/average-daily-pnl-per-sector.js +83 -75
- package/calculations/core/average-daily-pnl-per-stock.js +84 -73
- package/calculations/core/average-daily-position-pnl.js +2 -2
- package/calculations/core/holding-duration-per-asset.js +24 -23
- package/calculations/core/instrument-price-change-1d.js +72 -82
- package/calculations/core/instrument-price-momentum-20d.js +66 -100
- package/calculations/core/long-position-per-stock.js +21 -13
- package/calculations/core/overall-holding-duration.js +8 -3
- package/calculations/core/overall-profitability-ratio.js +2 -2
- package/calculations/core/platform-buy-sell-sentiment.js +75 -22
- package/calculations/core/platform-daily-bought-vs-sold-count.js +19 -10
- package/calculations/core/platform-daily-ownership-delta.js +39 -15
- package/calculations/core/platform-ownership-per-sector.js +38 -18
- package/calculations/core/platform-total-positions-held.js +36 -14
- package/calculations/core/pnl-distribution-per-stock.js +39 -36
- package/calculations/core/price-metrics.js +70 -172
- package/calculations/core/profitability-ratio-per-sector.js +23 -29
- package/calculations/core/profitability-ratio-per-stock.js +20 -13
- package/calculations/core/profitability-skew-per-stock.js +20 -13
- package/calculations/core/profitable-and-unprofitable-status.js +34 -10
- package/calculations/core/sentiment-per-stock.js +20 -9
- package/calculations/core/short-position-per-stock.js +23 -37
- package/calculations/core/social-activity-aggregation.js +41 -115
- package/calculations/core/social-asset-posts-trend.js +77 -94
- package/calculations/core/social-event-correlation.js +87 -106
- package/calculations/core/social-sentiment-aggregation.js +56 -138
- package/calculations/core/social-top-mentioned-words.js +74 -106
- package/calculations/core/social-topic-interest-evolution.js +94 -94
- package/calculations/core/social-topic-sentiment-matrix.js +90 -74
- package/calculations/core/social-word-mentions-trend.js +92 -106
- package/calculations/core/speculator-asset-sentiment.js +63 -92
- package/calculations/core/speculator-danger-zone.js +77 -90
- package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +75 -90
- package/calculations/core/speculator-distance-to-tp-per-leverage.js +75 -88
- package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +75 -90
- package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +74 -89
- package/calculations/core/speculator-leverage-per-asset.js +62 -57
- package/calculations/core/speculator-leverage-per-sector.js +53 -65
- package/calculations/core/speculator-risk-reward-ratio-per-asset.js +71 -76
- package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +60 -81
- package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +57 -77
- package/calculations/core/speculator-stop-loss-per-asset.js +43 -80
- package/calculations/core/speculator-take-profit-per-asset.js +45 -69
- package/calculations/core/speculator-tsl-per-asset.js +42 -49
- package/calculations/core/total-long-figures.js +19 -19
- package/calculations/core/total-long-per-sector.js +39 -36
- package/calculations/core/total-short-figures.js +19 -19
- package/calculations/core/total-short-per-sector.js +39 -36
- package/calculations/core/users-processed.js +52 -25
- package/calculations/gauss/cohort-capital-flow.js +38 -29
- package/calculations/gauss/cohort-definer.js +17 -25
- package/calculations/gauss/daily-dna-filter.js +10 -4
- package/calculations/gauss/gauss-divergence-signal.js +28 -6
- package/calculations/gem/cohort-momentum-state.js +113 -92
- package/calculations/gem/cohort-skill-definition.js +23 -53
- package/calculations/gem/platform-conviction-divergence.js +62 -116
- package/calculations/gem/quant-skill-alpha-signal.js +107 -123
- package/calculations/gem/skilled-cohort-flow.js +178 -167
- package/calculations/gem/skilled-unskilled-divergence.js +73 -113
- package/calculations/gem/unskilled-cohort-flow.js +176 -166
- package/calculations/helix/helix-contrarian-signal.js +91 -83
- package/calculations/helix/herd-consensus-score.js +135 -97
- package/calculations/helix/winner-loser-flow.js +14 -16
- package/calculations/pyro/risk-appetite-index.js +121 -123
- package/calculations/pyro/squeeze-potential.js +93 -125
- package/calculations/pyro/volatility-signal.js +109 -97
- package/package.json +5 -5
- package/README.MD +0 -155
|
@@ -1,145 +1,131 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 2 - Meta) for social word trend.
|
|
3
|
+
* --- FIX ---
|
|
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' (Array) to 'socialDoc' (Object).
|
|
7
|
+
* 4. Changed 'post.timestamp' to 'post.createdAt'.
|
|
8
|
+
* 5. Changed 'post.content' to 'post.textSnippet'.
|
|
8
9
|
*/
|
|
10
|
+
const STOPWORDS = new Set([
|
|
11
|
+
'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'being', 'been',
|
|
12
|
+
'it', 'its', 'it\'s', 'i', 'me', 'my', 'mine', 'you', 'your', 'yours',
|
|
13
|
+
'he', 'him', 'his', 'she', 'her', 'hers', 'they', 'them', 'their', 'theirs',
|
|
14
|
+
'we', 'us', 'our', 'ours', 'what', 'which', 'who', 'whom', 'this', 'that',
|
|
15
|
+
'these', 'those', 'am', 'and', 'but', 'if', 'or', 'because', 'as', 'until',
|
|
16
|
+
'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between',
|
|
17
|
+
'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to',
|
|
18
|
+
'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again',
|
|
19
|
+
'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how',
|
|
20
|
+
'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some',
|
|
21
|
+
'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too',
|
|
22
|
+
'very', 's', 't', 'can', 'will', 'just', 'don', 'should', 'now',
|
|
23
|
+
'stock', 'stocks', 'market', 'price', 'trade', 'trading', 'buy', 'sell',
|
|
24
|
+
'long', 'short', 'chart', 'week', 'day', 'today', 'going', 'think'
|
|
25
|
+
]);
|
|
26
|
+
|
|
9
27
|
class SocialWordMentionsTrend {
|
|
28
|
+
|
|
10
29
|
constructor() {
|
|
11
|
-
|
|
12
|
-
this.keywordData = new Map();
|
|
13
|
-
// Keywords are loaded from config
|
|
14
|
-
this.keywords = [];
|
|
30
|
+
this.result = {};
|
|
15
31
|
}
|
|
16
32
|
|
|
17
|
-
// --- NEW ---
|
|
18
|
-
/**
|
|
19
|
-
* Statically defines all metadata for the manifest builder.
|
|
20
|
-
*/
|
|
21
33
|
static getMetadata() {
|
|
22
34
|
return {
|
|
23
35
|
type: 'meta',
|
|
24
36
|
rootDataDependencies: ['social'],
|
|
25
|
-
isHistorical:
|
|
26
|
-
userType: 'n/a',
|
|
37
|
+
isHistorical: true, // Needs 7 days of posts
|
|
38
|
+
userType: 'n'/'a',
|
|
27
39
|
category: 'core_social'
|
|
28
40
|
};
|
|
29
41
|
}
|
|
30
42
|
|
|
31
|
-
// --- NEW ---
|
|
32
|
-
/**
|
|
33
|
-
* Statically declare dependencies.
|
|
34
|
-
*/
|
|
35
43
|
static getDependencies() {
|
|
36
|
-
return [];
|
|
44
|
+
return ['social-top-mentioned-words'];
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
/**
|
|
40
|
-
* Defines the output schema for this calculation.
|
|
41
|
-
* @returns {object} JSON Schema object
|
|
42
|
-
*/
|
|
43
47
|
static getSchema() {
|
|
44
|
-
const
|
|
48
|
+
const wordSchema = {
|
|
45
49
|
"type": "object",
|
|
46
|
-
"description": "Daily and trended post count for a specific keyword.",
|
|
47
50
|
"properties": {
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
"trend_30d_pct": {
|
|
53
|
-
"type": "number",
|
|
54
|
-
"description": "Percentage change from the 30-day average count."
|
|
55
|
-
},
|
|
56
|
-
"history_30d": {
|
|
57
|
-
"type": "array",
|
|
58
|
-
"description": "30-day history of mention counts (today first).",
|
|
59
|
-
"items": { "type": "number" }
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
"required": ["count", "trend_30d_pct", "history_30d"]
|
|
51
|
+
"count_1d": { "type": "number" },
|
|
52
|
+
"count_7d": { "type": "number" }
|
|
53
|
+
}
|
|
63
54
|
};
|
|
64
55
|
|
|
65
56
|
return {
|
|
66
57
|
"type": "object",
|
|
67
|
-
"description": "Tracks the
|
|
68
|
-
"patternProperties": {
|
|
69
|
-
|
|
70
|
-
},
|
|
71
|
-
"additionalProperties": keywordSchema
|
|
58
|
+
"description": "Tracks the 1-day vs 7-day mention count for the top 50 words.",
|
|
59
|
+
"patternProperties": { "^.*$": wordSchema },
|
|
60
|
+
"additionalProperties": wordSchema
|
|
72
61
|
};
|
|
73
62
|
}
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
64
|
+
_tokenize(text) {
|
|
65
|
+
if (!text) return [];
|
|
66
|
+
return text.toLowerCase()
|
|
67
|
+
.replace(/[^\w\s$]/g, '')
|
|
68
|
+
.split(/\s+/)
|
|
69
|
+
.filter(word =>
|
|
70
|
+
word.length > 2 &&
|
|
71
|
+
!STOPWORDS.has(word) &&
|
|
72
|
+
!/^\d+$/.test(word)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- THIS IS THE FIX ---
|
|
77
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
78
|
+
// dateStr is Arg 1 (metaPayload), rootData is Arg 2, dependencies is Arg 3
|
|
79
|
+
|
|
80
|
+
// Get social data from Arg 1 (worker hack)
|
|
81
|
+
const socialDoc = dateStr.social; // This IS the map of posts
|
|
82
|
+
// Get dependency from Arg 5
|
|
83
|
+
const topWordsData = fetchedDependencies['social-top-mentioned-words'];
|
|
84
|
+
|
|
85
|
+
if (!socialDoc || typeof socialDoc !== 'object' || !topWordsData) {
|
|
86
|
+
this.result = {};
|
|
87
|
+
return;
|
|
98
88
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.keywordData.get(normalizedKey).count++;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
89
|
+
|
|
90
|
+
const topWords = new Set(Object.keys(topWordsData));
|
|
91
|
+
// dateStr is the metaPayload, but it has a 'date' field
|
|
92
|
+
const today = new Date(dateStr.date + 'T00:00:00Z');
|
|
93
|
+
const sevenDaysAgo = new Date(today);
|
|
94
|
+
sevenDaysAgo.setUTCDate(today.getUTCDate() - 7);
|
|
95
|
+
|
|
96
|
+
const counts = new Map();
|
|
97
|
+
for (const word of topWords) {
|
|
98
|
+
counts.set(word, { count_1d: 0, count_7d: 0 });
|
|
112
99
|
}
|
|
113
|
-
}
|
|
114
100
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
101
|
+
for (const post of Object.values(socialDoc)) {
|
|
102
|
+
const postDate = new Date(post.createdAt); // Use 'createdAt'
|
|
103
|
+
const is1d = postDate.toISOString().slice(0, 10) === dateStr.date;
|
|
104
|
+
const is7d = postDate >= sevenDaysAgo;
|
|
105
|
+
|
|
106
|
+
if (!is7d) continue;
|
|
107
|
+
|
|
108
|
+
const words = this._tokenize(post.textSnippet); // Use 'textSnippet'
|
|
109
|
+
|
|
110
|
+
for (const word of words) {
|
|
111
|
+
if (counts.has(word)) {
|
|
112
|
+
const stats = counts.get(word);
|
|
113
|
+
if (is7d) stats.count_7d++;
|
|
114
|
+
if (is1d) stats.count_1d++;
|
|
128
115
|
}
|
|
129
116
|
}
|
|
130
|
-
|
|
131
|
-
result[keyword] = {
|
|
132
|
-
count: todayCount,
|
|
133
|
-
trend_30d_pct: trend_30d_pct,
|
|
134
|
-
history_30d: newHistory
|
|
135
|
-
};
|
|
136
117
|
}
|
|
137
|
-
|
|
118
|
+
|
|
119
|
+
this.result = Object.fromEntries(counts);
|
|
120
|
+
}
|
|
121
|
+
// --- END FIX ---
|
|
122
|
+
|
|
123
|
+
async getResult(fetchedDependencies) {
|
|
124
|
+
return this.result;
|
|
138
125
|
}
|
|
139
126
|
|
|
140
127
|
reset() {
|
|
141
|
-
this.
|
|
142
|
-
this.keywords = [];
|
|
128
|
+
this.result = {};
|
|
143
129
|
}
|
|
144
130
|
}
|
|
145
131
|
|
|
@@ -1,53 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator sentiment.
|
|
3
3
|
*
|
|
4
|
-
* This metric answers: "For each asset, what
|
|
5
|
-
*
|
|
6
|
-
* speculator positions?"
|
|
4
|
+
* This metric answers: "For each asset, what is the sentiment
|
|
5
|
+
* (long vs. short) *only* for speculators?"
|
|
7
6
|
*/
|
|
8
|
-
|
|
7
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
9
8
|
|
|
10
9
|
class SpeculatorAssetSentiment {
|
|
11
10
|
constructor() {
|
|
12
|
-
// { [instrumentId]: { long:
|
|
11
|
+
// { [instrumentId]: { long: 0, short: 0 } }
|
|
13
12
|
this.assets = new Map();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Defines the output schema for this calculation.
|
|
19
|
-
* @returns {object} JSON Schema object
|
|
20
|
-
*/
|
|
21
|
-
static getSchema() {
|
|
22
|
-
const sentimentDetailSchema = {
|
|
23
|
-
"type": "object",
|
|
24
|
-
"properties": {
|
|
25
|
-
"avg_pnl_pct": { "type": "number" },
|
|
26
|
-
"avg_leverage": { "type": "number" },
|
|
27
|
-
"sl_rate_pct": { "type": "number" },
|
|
28
|
-
"tp_rate_pct": { "type": "number" },
|
|
29
|
-
"count": { "type": "number" }
|
|
30
|
-
},
|
|
31
|
-
"required": ["avg_pnl_pct", "avg_leverage", "sl_rate_pct", "tp_rate_pct", "count"]
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const tickerSchema = {
|
|
35
|
-
"type": "object",
|
|
36
|
-
"properties": {
|
|
37
|
-
"long": sentimentDetailSchema,
|
|
38
|
-
"short": sentimentDetailSchema
|
|
39
|
-
},
|
|
40
|
-
"required": ["long", "short"]
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
"type": "object",
|
|
45
|
-
"description": "Aggregates P&L, leverage, and SL/TP usage for speculators, split by long/short positions per asset.",
|
|
46
|
-
"patternProperties": {
|
|
47
|
-
"^.*$": tickerSchema // Ticker
|
|
48
|
-
},
|
|
49
|
-
"additionalProperties": tickerSchema
|
|
50
|
-
};
|
|
13
|
+
// --- STANDARD 0: RENAMED ---
|
|
14
|
+
this.tickerMap = null;
|
|
51
15
|
}
|
|
52
16
|
|
|
53
17
|
/**
|
|
@@ -58,8 +22,8 @@ class SpeculatorAssetSentiment {
|
|
|
58
22
|
type: 'standard',
|
|
59
23
|
rootDataDependencies: ['portfolio'],
|
|
60
24
|
isHistorical: false,
|
|
61
|
-
userType: 'speculator',
|
|
62
|
-
category: '
|
|
25
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
26
|
+
category: 'core_sentiment'
|
|
63
27
|
};
|
|
64
28
|
}
|
|
65
29
|
|
|
@@ -70,28 +34,43 @@ class SpeculatorAssetSentiment {
|
|
|
70
34
|
return [];
|
|
71
35
|
}
|
|
72
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Defines the output schema for this calculation.
|
|
39
|
+
*/
|
|
40
|
+
static getSchema() {
|
|
41
|
+
const tickerSchema = {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"long_count": { "type": "number" },
|
|
45
|
+
"short_count": { "type": "number" },
|
|
46
|
+
"sentiment_ratio": { "type": ["number", "null"] }
|
|
47
|
+
},
|
|
48
|
+
"required": ["long_count", "short_count", "sentiment_ratio"]
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"description": "Calculates the long/short sentiment ratio *only* for speculators.",
|
|
54
|
+
"patternProperties": { "^.*$": tickerSchema },
|
|
55
|
+
"additionalProperties": tickerSchema
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
73
59
|
_initAsset(instrumentId) {
|
|
74
60
|
if (!this.assets.has(instrumentId)) {
|
|
75
|
-
|
|
76
|
-
pnl_sum: 0,
|
|
77
|
-
leverage_sum: 0,
|
|
78
|
-
sl_set_count: 0,
|
|
79
|
-
tp_set_count: 0,
|
|
80
|
-
count: 0
|
|
81
|
-
});
|
|
82
|
-
this.assets.set(instrumentId, {
|
|
83
|
-
long: createSide(),
|
|
84
|
-
short: createSide()
|
|
85
|
-
});
|
|
61
|
+
this.assets.set(instrumentId, { long: 0, short: 0 });
|
|
86
62
|
}
|
|
87
63
|
}
|
|
88
64
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
65
|
+
// --- STANDARD 0: UPDATED SIGNATURE ---
|
|
66
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
67
|
+
// --- STANDARD 0: ADDED ---
|
|
68
|
+
if (!this.tickerMap) {
|
|
69
|
+
this.tickerMap = context.instrumentToTicker;
|
|
92
70
|
}
|
|
93
|
-
|
|
94
|
-
|
|
71
|
+
|
|
72
|
+
// Only speculators are processed, so we can assume PublicPositions exists
|
|
73
|
+
const positions = todayPortfolio.PublicPositions;
|
|
95
74
|
if (!positions || !Array.isArray(positions)) {
|
|
96
75
|
return;
|
|
97
76
|
}
|
|
@@ -101,52 +80,44 @@ class SpeculatorAssetSentiment {
|
|
|
101
80
|
if (!instrumentId) continue;
|
|
102
81
|
|
|
103
82
|
this._initAsset(instrumentId);
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (pos.TakeProfitRate) sideData.tp_set_count++;
|
|
112
|
-
sideData.count++;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
_calculateAverages(data) {
|
|
117
|
-
const count = data.count;
|
|
118
|
-
if (count === 0) {
|
|
119
|
-
return { avg_pnl_pct: 0, avg_leverage: 0, sl_rate_pct: 0, tp_rate_pct: 0, count: 0 };
|
|
83
|
+
const assetData = this.assets.get(instrumentId);
|
|
84
|
+
|
|
85
|
+
if (pos.IsBuy) {
|
|
86
|
+
assetData.long++;
|
|
87
|
+
} else {
|
|
88
|
+
assetData.short++;
|
|
89
|
+
}
|
|
120
90
|
}
|
|
121
|
-
return {
|
|
122
|
-
avg_pnl_pct: data.pnl_sum / count,
|
|
123
|
-
avg_leverage: data.leverage_sum / count,
|
|
124
|
-
sl_rate_pct: (data.sl_set_count / count) * 100,
|
|
125
|
-
tp_rate_pct: (data.tp_set_count / count) * 100,
|
|
126
|
-
count: count
|
|
127
|
-
};
|
|
128
91
|
}
|
|
129
92
|
|
|
130
93
|
async getResult() {
|
|
131
|
-
|
|
132
|
-
|
|
94
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
95
|
+
|
|
96
|
+
// Failsafe check
|
|
97
|
+
if (!this.tickerMap) {
|
|
98
|
+
return {}; // process() must run first
|
|
133
99
|
}
|
|
134
100
|
|
|
135
101
|
const result = {};
|
|
136
102
|
for (const [instrumentId, data] of this.assets.entries()) {
|
|
137
|
-
|
|
103
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
104
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
138
105
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
106
|
+
if (data.long > 0 || data.short > 0) {
|
|
107
|
+
result[ticker] = {
|
|
108
|
+
long_count: data.long,
|
|
109
|
+
short_count: data.short,
|
|
110
|
+
sentiment_ratio: (data.short > 0) ? (data.long / data.short) : null
|
|
111
|
+
};
|
|
112
|
+
}
|
|
143
113
|
}
|
|
144
114
|
return result;
|
|
145
115
|
}
|
|
146
116
|
|
|
147
117
|
reset() {
|
|
148
118
|
this.assets.clear();
|
|
149
|
-
|
|
119
|
+
// --- STANDARD 0: RENAMED ---
|
|
120
|
+
this.tickerMap = null;
|
|
150
121
|
}
|
|
151
122
|
}
|
|
152
123
|
|