aiden-shared-calculations-unified 1.0.7 → 1.0.9
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/pnl/average_daily_pnl_all_users.js +1 -1
- package/calculations/pnl/average_daily_pnl_per_sector.js +1 -1
- package/calculations/pnl/average_daily_pnl_per_stock.js +1 -1
- package/calculations/pnl/average_daily_position_pnl.js +1 -1
- package/calculations/pnl/pnl_distribution_per_stock.js +52 -52
- package/calculations/pnl/profitability_ratio_per_stock.js +50 -50
- package/calculations/pnl/profitability_skew_per_stock.js +58 -58
- package/calculations/sanity/users_processed.js +26 -26
- package/calculations/sectors/total_long_per_sector.js +1 -1
- package/calculations/sectors/total_short_per_sector.js +1 -1
- package/calculations/short_and_long_stats/long_position_per_stock.js +1 -1
- package/calculations/short_and_long_stats/sentiment_per_stock.js +49 -49
- package/calculations/short_and_long_stats/short_position_per_stock.js +1 -1
- package/calculations/short_and_long_stats/total_long_figures.js +1 -1
- package/calculations/short_and_long_stats/total_short_figures.js +1 -1
- package/calculations/socialPosts/social-asset-posts-trend.js +53 -0
- package/calculations/socialPosts/social-top-mentioned-words.js +103 -0
- package/calculations/socialPosts/social-topic-interest-evolution.js +54 -0
- package/calculations/socialPosts/social-word-mentions-trend.js +63 -0
- package/calculations/speculators/distance_to_stop_loss_per_leverage.js +78 -78
- package/calculations/speculators/distance_to_tp_per_leverage.js +76 -76
- package/calculations/speculators/entry_distance_to_sl_per_leverage.js +78 -78
- package/calculations/speculators/entry_distance_to_tp_per_leverage.js +77 -77
- package/calculations/speculators/holding_duration_per_asset.js +55 -55
- package/calculations/speculators/leverage_per_asset.js +46 -46
- package/calculations/speculators/leverage_per_sector.js +44 -44
- package/calculations/speculators/risk_reward_ratio_per_asset.js +60 -60
- package/calculations/speculators/speculator_asset_sentiment.js +81 -81
- package/calculations/speculators/speculator_danger_zone.js +57 -57
- package/calculations/speculators/stop_loss_distance_by_sector_short_long_breakdown.js +91 -91
- package/calculations/speculators/stop_loss_distance_by_ticker_short_long_breakdown.js +73 -73
- package/calculations/speculators/stop_loss_per_asset.js +55 -55
- package/calculations/speculators/take_profit_per_asset.js +55 -55
- package/calculations/speculators/tsl_per_asset.js +51 -51
- package/package.json +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Finds the top mentioned words in all posts for the day,
|
|
3
|
+
* filtering out common stop words, and calculates the trend from yesterday.
|
|
4
|
+
* This calculation ONLY uses the social post insights context.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class SocialTopMentionedWords {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.todayWordCounts = {};
|
|
10
|
+
this.yesterdayWordCounts = {};
|
|
11
|
+
this.processed = false;
|
|
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
|
+
]);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Helper to process posts and populate a word count map.
|
|
27
|
+
* @param {object} socialPostInsights - Map of { [postId]: postData }
|
|
28
|
+
* @param {object} targetCountMap - The object to store word counts.
|
|
29
|
+
*/
|
|
30
|
+
_countWords(socialPostInsights, targetCountMap) {
|
|
31
|
+
if (!socialPostInsights) return;
|
|
32
|
+
const posts = Object.values(socialPostInsights);
|
|
33
|
+
|
|
34
|
+
for (const post of posts) {
|
|
35
|
+
const text = post.fullText?.toLowerCase() || '';
|
|
36
|
+
if (!text) continue;
|
|
37
|
+
|
|
38
|
+
// Get all words 3+ chars long
|
|
39
|
+
const words = text.match(/\b[a-z]{3,}\b/g) || [];
|
|
40
|
+
|
|
41
|
+
for (const word of words) {
|
|
42
|
+
if (!this.stopWords.has(word)) {
|
|
43
|
+
targetCountMap[word] = (targetCountMap[word] || 0) + 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
|
|
51
|
+
* @param {object} yesterdaySocialPostInsights - Map of { [postId]: postData } for yesterday.
|
|
52
|
+
*/
|
|
53
|
+
async process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights) {
|
|
54
|
+
|
|
55
|
+
if (this.processed) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.processed = true;
|
|
59
|
+
|
|
60
|
+
// Process today's posts
|
|
61
|
+
this._countWords(todaySocialPostInsights, this.todayWordCounts);
|
|
62
|
+
|
|
63
|
+
// Process yesterday's posts for trend calculation
|
|
64
|
+
this._countWords(yesterdaySocialPostInsights, this.yesterdayWordCounts);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async getResult() {
|
|
68
|
+
if (Object.keys(this.todayWordCounts).length === 0) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const totalTodayWords = Object.values(this.todayWordCounts).reduce((a, b) => a + b, 0);
|
|
73
|
+
|
|
74
|
+
const topWords = Object.entries(this.todayWordCounts)
|
|
75
|
+
.sort(([, countA], [, countB]) => countB - countA)
|
|
76
|
+
.slice(0, 50); // Return the Top 50
|
|
77
|
+
|
|
78
|
+
const result = topWords.map(([term, count]) => {
|
|
79
|
+
const yesterdayCount = this.yesterdayWordCounts[term] || 0;
|
|
80
|
+
const trend = count - yesterdayCount;
|
|
81
|
+
const percent = (count / totalTodayWords) * 100;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
term,
|
|
85
|
+
count,
|
|
86
|
+
percent: parseFloat(percent.toFixed(2)),
|
|
87
|
+
trend
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Returns an array like:
|
|
92
|
+
// [ { term: "btc", count: 150, percent: 5.2, trend: 25 }, ... ]
|
|
93
|
+
return { top_words: result };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
reset() {
|
|
97
|
+
this.todayWordCounts = {};
|
|
98
|
+
this.yesterdayWordCounts = {};
|
|
99
|
+
this.processed = false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = SocialTopMentionedWords;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Aggregates counts of all topics extracted by the AI
|
|
3
|
+
* from social posts for the day. Used to build a "Topic Interest Area" chart.
|
|
4
|
+
* This calculation ONLY uses the social post insights context.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class SocialTopicInterestEvolution {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.topicCounts = {};
|
|
10
|
+
this.processed = false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {null} todayPortfolio - Not used.
|
|
15
|
+
* ... (other params not used) ...
|
|
16
|
+
* @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
|
|
17
|
+
* @param {object} yesterdaySocialPostInsights - Not used.
|
|
18
|
+
*/
|
|
19
|
+
async process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights) {
|
|
20
|
+
|
|
21
|
+
if (this.processed || !todaySocialPostInsights) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.processed = true;
|
|
25
|
+
|
|
26
|
+
const posts = Object.values(todaySocialPostInsights);
|
|
27
|
+
|
|
28
|
+
for (const post of posts) {
|
|
29
|
+
// Topics are in post.sentiment.topics
|
|
30
|
+
const topics = post.sentiment?.topics;
|
|
31
|
+
if (!topics || !Array.isArray(topics)) continue;
|
|
32
|
+
|
|
33
|
+
for (const topic of topics) {
|
|
34
|
+
// Normalize topic for consistent aggregation
|
|
35
|
+
const normalizedTopic = String(topic).toLowerCase().trim();
|
|
36
|
+
if (normalizedTopic) {
|
|
37
|
+
this.topicCounts[normalizedTopic] = (this.topicCounts[normalizedTopic] || 0) + 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async getResult() {
|
|
44
|
+
// Returns an object like: { "earnings": 45, "cpi": 12, "fed": 22 }
|
|
45
|
+
return this.topicCounts;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
reset() {
|
|
49
|
+
this.topicCounts = {};
|
|
50
|
+
this.processed = false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = SocialTopicInterestEvolution;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tracks daily mentions of a pre-defined list of keywords.
|
|
3
|
+
* This calculation ONLY uses the social post insights context.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class SocialWordMentionsTrend {
|
|
7
|
+
constructor() {
|
|
8
|
+
// A pre-defined list of keywords to track
|
|
9
|
+
this.KEYWORDS = [
|
|
10
|
+
'inflation', 'fed', 'fomc', 'cpi', 'earnings',
|
|
11
|
+
'recession', 'bullish', 'bearish', 'buy', 'sell',
|
|
12
|
+
'war', 'acquisition', 'ai'
|
|
13
|
+
];
|
|
14
|
+
this.mentions = {};
|
|
15
|
+
this.processed = false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {null} todayPortfolio - Not used.
|
|
20
|
+
* ... (other params not used) ...
|
|
21
|
+
* @param {object} todaySocialPostInsights - Map of { [postId]: postData } for today.
|
|
22
|
+
* @param {object} yesterdaySocialPostInsights - Not used.
|
|
23
|
+
*/
|
|
24
|
+
async process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights) {
|
|
25
|
+
|
|
26
|
+
if (this.processed || !todaySocialPostInsights) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.processed = true;
|
|
30
|
+
|
|
31
|
+
// Initialize all keywords with 0 counts
|
|
32
|
+
this.mentions = this.KEYWORDS.reduce((acc, key) => {
|
|
33
|
+
acc[key] = 0;
|
|
34
|
+
return acc;
|
|
35
|
+
}, {});
|
|
36
|
+
|
|
37
|
+
const posts = Object.values(todaySocialPostInsights);
|
|
38
|
+
|
|
39
|
+
for (const post of posts) {
|
|
40
|
+
const text = post.fullText?.toLowerCase() || '';
|
|
41
|
+
if (!text) continue;
|
|
42
|
+
|
|
43
|
+
for (const keyword of this.KEYWORDS) {
|
|
44
|
+
// Count once per post, even if mentioned multiple times
|
|
45
|
+
if (text.includes(keyword)) {
|
|
46
|
+
this.mentions[keyword]++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getResult() {
|
|
53
|
+
// Returns an object like: { "inflation": 5, "fed": 12, "ai": 30 }
|
|
54
|
+
return this.mentions;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
reset() {
|
|
58
|
+
this.mentions = {};
|
|
59
|
+
this.processed = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = SocialWordMentionsTrend;
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculates the average distance to the stop loss from the current price for
|
|
3
|
-
* both long and short positions, grouped by asset and leverage level.
|
|
4
|
-
* It ignores near-zero stop loss values.
|
|
5
|
-
*/
|
|
6
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
|
-
|
|
8
|
-
class DistanceToStopLossPerLeverage {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.stopLossData = {};
|
|
11
|
-
this.mappings = null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
process(portfolioData, userId) {
|
|
15
|
-
if (portfolioData && portfolioData.PublicPositions) {
|
|
16
|
-
for (const position of portfolioData.PublicPositions) {
|
|
17
|
-
const instrumentId = position.InstrumentID;
|
|
18
|
-
const leverage = position.Leverage;
|
|
19
|
-
const stopLossRate = position.StopLossRate;
|
|
20
|
-
const currentRate = position.CurrentRate;
|
|
21
|
-
const isBuy = position.IsBuy;
|
|
22
|
-
|
|
23
|
-
if (stopLossRate > 0.0001 && currentRate > 0) {
|
|
24
|
-
let distance;
|
|
25
|
-
|
|
26
|
-
if (isBuy) {
|
|
27
|
-
distance = currentRate - stopLossRate;
|
|
28
|
-
} else {
|
|
29
|
-
distance = stopLossRate - currentRate;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const distancePercent = (distance / currentRate) * 100;
|
|
33
|
-
|
|
34
|
-
if (distancePercent > 0) {
|
|
35
|
-
if (!this.stopLossData[instrumentId]) {
|
|
36
|
-
this.stopLossData[instrumentId] = {};
|
|
37
|
-
}
|
|
38
|
-
if (!this.stopLossData[instrumentId][leverage]) {
|
|
39
|
-
this.stopLossData[instrumentId][leverage] = { distance_sum_percent: 0, count: 0 };
|
|
40
|
-
}
|
|
41
|
-
this.stopLossData[instrumentId][leverage].distance_sum_percent += distancePercent;
|
|
42
|
-
this.stopLossData[instrumentId][leverage].count++;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async getResult() {
|
|
50
|
-
if (!this.mappings) {
|
|
51
|
-
this.mappings = await loadInstrumentMappings();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const result = {};
|
|
55
|
-
for (const instrumentId in this.stopLossData) {
|
|
56
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
57
|
-
result[ticker] = {};
|
|
58
|
-
for (const leverage in this.stopLossData[instrumentId]) {
|
|
59
|
-
const data = this.stopLossData[instrumentId][leverage];
|
|
60
|
-
// REFACTOR: Perform final calculation directly.
|
|
61
|
-
if (data.count > 0) {
|
|
62
|
-
result[ticker][leverage] = {
|
|
63
|
-
average_distance_percent: data.distance_sum_percent / data.count,
|
|
64
|
-
count: data.count
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
reset() {
|
|
73
|
-
this.stopLossData = {};
|
|
74
|
-
this.mappings = null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
module.exports = DistanceToStopLossPerLeverage;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Calculates the average distance to the stop loss from the current price for
|
|
3
|
+
* both long and short positions, grouped by asset and leverage level.
|
|
4
|
+
* It ignores near-zero stop loss values.
|
|
5
|
+
*/
|
|
6
|
+
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
|
+
|
|
8
|
+
class DistanceToStopLossPerLeverage {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.stopLossData = {};
|
|
11
|
+
this.mappings = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
process(portfolioData, yesterdayPortfolio, userId, context) {
|
|
15
|
+
if (portfolioData && portfolioData.PublicPositions) {
|
|
16
|
+
for (const position of portfolioData.PublicPositions) {
|
|
17
|
+
const instrumentId = position.InstrumentID;
|
|
18
|
+
const leverage = position.Leverage;
|
|
19
|
+
const stopLossRate = position.StopLossRate;
|
|
20
|
+
const currentRate = position.CurrentRate;
|
|
21
|
+
const isBuy = position.IsBuy;
|
|
22
|
+
|
|
23
|
+
if (stopLossRate > 0.0001 && currentRate > 0) {
|
|
24
|
+
let distance;
|
|
25
|
+
|
|
26
|
+
if (isBuy) {
|
|
27
|
+
distance = currentRate - stopLossRate;
|
|
28
|
+
} else {
|
|
29
|
+
distance = stopLossRate - currentRate;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const distancePercent = (distance / currentRate) * 100;
|
|
33
|
+
|
|
34
|
+
if (distancePercent > 0) {
|
|
35
|
+
if (!this.stopLossData[instrumentId]) {
|
|
36
|
+
this.stopLossData[instrumentId] = {};
|
|
37
|
+
}
|
|
38
|
+
if (!this.stopLossData[instrumentId][leverage]) {
|
|
39
|
+
this.stopLossData[instrumentId][leverage] = { distance_sum_percent: 0, count: 0 };
|
|
40
|
+
}
|
|
41
|
+
this.stopLossData[instrumentId][leverage].distance_sum_percent += distancePercent;
|
|
42
|
+
this.stopLossData[instrumentId][leverage].count++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async getResult() {
|
|
50
|
+
if (!this.mappings) {
|
|
51
|
+
this.mappings = await loadInstrumentMappings();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = {};
|
|
55
|
+
for (const instrumentId in this.stopLossData) {
|
|
56
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
57
|
+
result[ticker] = {};
|
|
58
|
+
for (const leverage in this.stopLossData[instrumentId]) {
|
|
59
|
+
const data = this.stopLossData[instrumentId][leverage];
|
|
60
|
+
// REFACTOR: Perform final calculation directly.
|
|
61
|
+
if (data.count > 0) {
|
|
62
|
+
result[ticker][leverage] = {
|
|
63
|
+
average_distance_percent: data.distance_sum_percent / data.count,
|
|
64
|
+
count: data.count
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
reset() {
|
|
73
|
+
this.stopLossData = {};
|
|
74
|
+
this.mappings = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = DistanceToStopLossPerLeverage;
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculates the average distance to the take profit from the current price for
|
|
3
|
-
* both long and short positions, grouped by asset and leverage level.
|
|
4
|
-
* It ignores positions where take profit is not set.
|
|
5
|
-
*/
|
|
6
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
|
-
|
|
8
|
-
class DistanceToTakeProfitPerLeverage {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.takeProfitData = {};
|
|
11
|
-
this.mappings = null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
process(portfolioData, userId) {
|
|
15
|
-
if (portfolioData && portfolioData.PublicPositions) {
|
|
16
|
-
for (const position of portfolioData.PublicPositions) {
|
|
17
|
-
const instrumentId = position.InstrumentID;
|
|
18
|
-
const leverage = position.Leverage;
|
|
19
|
-
const takeProfitRate = position.TakeProfitRate;
|
|
20
|
-
const currentRate = position.CurrentRate;
|
|
21
|
-
const isBuy = position.IsBuy;
|
|
22
|
-
|
|
23
|
-
if (takeProfitRate > 0.0001 && currentRate > 0) {
|
|
24
|
-
let distance;
|
|
25
|
-
if (isBuy) {
|
|
26
|
-
distance = takeProfitRate - currentRate;
|
|
27
|
-
} else {
|
|
28
|
-
distance = currentRate - takeProfitRate;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const distancePercent = (distance / currentRate) * 100;
|
|
32
|
-
|
|
33
|
-
if (distancePercent > 0) {
|
|
34
|
-
if (!this.takeProfitData[instrumentId]) {
|
|
35
|
-
this.takeProfitData[instrumentId] = {};
|
|
36
|
-
}
|
|
37
|
-
if (!this.takeProfitData[instrumentId][leverage]) {
|
|
38
|
-
this.takeProfitData[instrumentId][leverage] = { distance_sum_percent: 0, count: 0 };
|
|
39
|
-
}
|
|
40
|
-
this.takeProfitData[instrumentId][leverage].distance_sum_percent += distancePercent;
|
|
41
|
-
this.takeProfitData[instrumentId][leverage].count++;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async getResult() {
|
|
49
|
-
if (!this.mappings) {
|
|
50
|
-
this.mappings = await loadInstrumentMappings();
|
|
51
|
-
}
|
|
52
|
-
const result = {};
|
|
53
|
-
for (const instrumentId in this.takeProfitData) {
|
|
54
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
55
|
-
result[ticker] = {};
|
|
56
|
-
for (const leverage in this.takeProfitData[instrumentId]) {
|
|
57
|
-
const data = this.takeProfitData[instrumentId][leverage];
|
|
58
|
-
// REFACTOR: Perform final calculation directly.
|
|
59
|
-
if (data.count > 0) {
|
|
60
|
-
result[ticker][leverage] = {
|
|
61
|
-
average_distance_percent: data.distance_sum_percent / data.count,
|
|
62
|
-
count: data.count
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
reset() {
|
|
71
|
-
this.takeProfitData = {};
|
|
72
|
-
this.mappings = null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
module.exports = DistanceToTakeProfitPerLeverage;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Calculates the average distance to the take profit from the current price for
|
|
3
|
+
* both long and short positions, grouped by asset and leverage level.
|
|
4
|
+
* It ignores positions where take profit is not set.
|
|
5
|
+
*/
|
|
6
|
+
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
|
+
|
|
8
|
+
class DistanceToTakeProfitPerLeverage {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.takeProfitData = {};
|
|
11
|
+
this.mappings = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
process(portfolioData, yesterdayPortfolio, userId, context) {
|
|
15
|
+
if (portfolioData && portfolioData.PublicPositions) {
|
|
16
|
+
for (const position of portfolioData.PublicPositions) {
|
|
17
|
+
const instrumentId = position.InstrumentID;
|
|
18
|
+
const leverage = position.Leverage;
|
|
19
|
+
const takeProfitRate = position.TakeProfitRate;
|
|
20
|
+
const currentRate = position.CurrentRate;
|
|
21
|
+
const isBuy = position.IsBuy;
|
|
22
|
+
|
|
23
|
+
if (takeProfitRate > 0.0001 && currentRate > 0) {
|
|
24
|
+
let distance;
|
|
25
|
+
if (isBuy) {
|
|
26
|
+
distance = takeProfitRate - currentRate;
|
|
27
|
+
} else {
|
|
28
|
+
distance = currentRate - takeProfitRate;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const distancePercent = (distance / currentRate) * 100;
|
|
32
|
+
|
|
33
|
+
if (distancePercent > 0) {
|
|
34
|
+
if (!this.takeProfitData[instrumentId]) {
|
|
35
|
+
this.takeProfitData[instrumentId] = {};
|
|
36
|
+
}
|
|
37
|
+
if (!this.takeProfitData[instrumentId][leverage]) {
|
|
38
|
+
this.takeProfitData[instrumentId][leverage] = { distance_sum_percent: 0, count: 0 };
|
|
39
|
+
}
|
|
40
|
+
this.takeProfitData[instrumentId][leverage].distance_sum_percent += distancePercent;
|
|
41
|
+
this.takeProfitData[instrumentId][leverage].count++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getResult() {
|
|
49
|
+
if (!this.mappings) {
|
|
50
|
+
this.mappings = await loadInstrumentMappings();
|
|
51
|
+
}
|
|
52
|
+
const result = {};
|
|
53
|
+
for (const instrumentId in this.takeProfitData) {
|
|
54
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
55
|
+
result[ticker] = {};
|
|
56
|
+
for (const leverage in this.takeProfitData[instrumentId]) {
|
|
57
|
+
const data = this.takeProfitData[instrumentId][leverage];
|
|
58
|
+
// REFACTOR: Perform final calculation directly.
|
|
59
|
+
if (data.count > 0) {
|
|
60
|
+
result[ticker][leverage] = {
|
|
61
|
+
average_distance_percent: data.distance_sum_percent / data.count,
|
|
62
|
+
count: data.count
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
reset() {
|
|
71
|
+
this.takeProfitData = {};
|
|
72
|
+
this.mappings = null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = DistanceToTakeProfitPerLeverage;
|