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,48 +1,89 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator metric.
|
|
3
|
+
*
|
|
4
|
+
* This metric answers: "For each asset and leverage level,
|
|
5
|
+
* what is the average percentage distance from the *current price*
|
|
6
|
+
* to the stop loss?"
|
|
5
7
|
*/
|
|
6
8
|
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
9
|
|
|
8
10
|
class DistanceToStopLossPerLeverage {
|
|
9
11
|
constructor() {
|
|
10
|
-
|
|
12
|
+
// { [instrumentId]: { [leverage]: { sum_dist: 0, count: 0 } } }
|
|
13
|
+
this.assets = new Map();
|
|
11
14
|
this.mappings = null;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Defines the output schema for this calculation.
|
|
19
|
+
* @returns {object} JSON Schema object
|
|
20
|
+
*/
|
|
21
|
+
static getSchema() {
|
|
22
|
+
const leverageSchema = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"description": "Average SL distance grouped by leverage.",
|
|
25
|
+
"patternProperties": {
|
|
26
|
+
// Leverage level, e.g., "1x", "5x", "10x"
|
|
27
|
+
"^[0-9]+x$": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"description": "The average percentage distance from current price to Stop Loss for this leverage level."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": { "type": "number" }
|
|
33
|
+
};
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
return {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"description": "Calculates avg % distance from current price to SL, bucketed by asset and leverage.",
|
|
38
|
+
"patternProperties": {
|
|
39
|
+
"^.*$": leverageSchema // Ticker
|
|
40
|
+
},
|
|
41
|
+
"additionalProperties": leverageSchema
|
|
42
|
+
};
|
|
43
|
+
}
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
_initLeverage(assetData, leverage) {
|
|
46
|
+
const key = `${leverage}x`;
|
|
47
|
+
if (!assetData[key]) {
|
|
48
|
+
assetData[key] = { sum_dist: 0, count: 0 };
|
|
49
|
+
}
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
33
52
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
process(portfolioData) {
|
|
54
|
+
// This calculation is only for speculators
|
|
55
|
+
if (portfolioData?.context?.userType !== 'speculator') {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const positions = portfolioData.PublicPositions;
|
|
60
|
+
if (!positions || !Array.isArray(positions)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const pos of positions) {
|
|
65
|
+
const instrumentId = pos.InstrumentID;
|
|
66
|
+
const sl_rate = pos.StopLossRate || 0;
|
|
67
|
+
const current_price = pos.LastCloseRate || 0; // Use last close as current price
|
|
68
|
+
|
|
69
|
+
if (!instrumentId || sl_rate === 0 || current_price === 0) {
|
|
70
|
+
continue; // Skip positions without SL or price
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!this.assets.has(instrumentId)) {
|
|
74
|
+
this.assets.set(instrumentId, {});
|
|
45
75
|
}
|
|
76
|
+
const assetData = this.assets.get(instrumentId);
|
|
77
|
+
|
|
78
|
+
const leverage = pos.Leverage || 1;
|
|
79
|
+
const key = this._initLeverage(assetData, leverage);
|
|
80
|
+
|
|
81
|
+
// Calculate distance
|
|
82
|
+
const distance = Math.abs(current_price - sl_rate);
|
|
83
|
+
const distance_pct = (distance / current_price);
|
|
84
|
+
|
|
85
|
+
assetData[key].sum_dist += distance_pct;
|
|
86
|
+
assetData[key].count++;
|
|
46
87
|
}
|
|
47
88
|
}
|
|
48
89
|
|
|
@@ -52,17 +93,15 @@ class DistanceToStopLossPerLeverage {
|
|
|
52
93
|
}
|
|
53
94
|
|
|
54
95
|
const result = {};
|
|
55
|
-
for (const instrumentId
|
|
56
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId
|
|
96
|
+
for (const [instrumentId, leverageData] of this.assets.entries()) {
|
|
97
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
|
|
57
98
|
result[ticker] = {};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
99
|
+
|
|
100
|
+
for (const leverageKey in leverageData) {
|
|
101
|
+
const data = leverageData[leverageKey];
|
|
61
102
|
if (data.count > 0) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
count: data.count
|
|
65
|
-
};
|
|
103
|
+
// Store the final average distance * 100 for percentage
|
|
104
|
+
result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
|
|
66
105
|
}
|
|
67
106
|
}
|
|
68
107
|
}
|
|
@@ -70,9 +109,9 @@ class DistanceToStopLossPerLeverage {
|
|
|
70
109
|
}
|
|
71
110
|
|
|
72
111
|
reset() {
|
|
73
|
-
this.
|
|
112
|
+
this.assets.clear();
|
|
74
113
|
this.mappings = null;
|
|
75
114
|
}
|
|
76
115
|
}
|
|
77
116
|
|
|
78
|
-
module.exports = DistanceToStopLossPerLeverage;
|
|
117
|
+
module.exports = DistanceToStopLossPerLeverage;
|
|
@@ -1,47 +1,88 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator metric.
|
|
3
|
+
*
|
|
4
|
+
* This metric answers: "For each asset and leverage level,
|
|
5
|
+
* what is the average percentage distance from the *current price*
|
|
6
|
+
* to the take profit?"
|
|
5
7
|
*/
|
|
6
8
|
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
9
|
|
|
8
10
|
class DistanceToTakeProfitPerLeverage {
|
|
9
11
|
constructor() {
|
|
10
|
-
|
|
12
|
+
// { [instrumentId]: { [leverage]: { sum_dist: 0, count: 0 } } }
|
|
13
|
+
this.assets = new Map();
|
|
11
14
|
this.mappings = null;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Defines the output schema for this calculation.
|
|
19
|
+
* @returns {object} JSON Schema object
|
|
20
|
+
*/
|
|
21
|
+
static getSchema() {
|
|
22
|
+
const leverageSchema = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"description": "Average TP distance grouped by leverage.",
|
|
25
|
+
"patternProperties": {
|
|
26
|
+
// Leverage level, e.g., "1x", "5x", "10x"
|
|
27
|
+
"^[0-9]+x$": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"description": "The average percentage distance from current price to Take Profit for this leverage level."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": { "type": "number" }
|
|
33
|
+
};
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
return {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"description": "Calculates avg % distance from current price to TP, bucketed by asset and leverage.",
|
|
38
|
+
"patternProperties": {
|
|
39
|
+
"^.*$": leverageSchema // Ticker
|
|
40
|
+
},
|
|
41
|
+
"additionalProperties": leverageSchema
|
|
42
|
+
};
|
|
43
|
+
}
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
_initLeverage(assetData, leverage) {
|
|
46
|
+
const key = `${leverage}x`;
|
|
47
|
+
if (!assetData[key]) {
|
|
48
|
+
assetData[key] = { sum_dist: 0, count: 0 };
|
|
49
|
+
}
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
32
52
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
process(portfolioData) {
|
|
54
|
+
// This calculation is only for speculators
|
|
55
|
+
if (portfolioData?.context?.userType !== 'speculator') {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const positions = portfolioData.PublicPositions;
|
|
60
|
+
if (!positions || !Array.isArray(positions)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const pos of positions) {
|
|
65
|
+
const instrumentId = pos.InstrumentID;
|
|
66
|
+
const tp_rate = pos.TakeProfitRate || 0;
|
|
67
|
+
const current_price = pos.LastCloseRate || 0;
|
|
68
|
+
|
|
69
|
+
if (!instrumentId || tp_rate === 0 || current_price === 0) {
|
|
70
|
+
continue; // Skip positions without TP or price
|
|
44
71
|
}
|
|
72
|
+
|
|
73
|
+
if (!this.assets.has(instrumentId)) {
|
|
74
|
+
this.assets.set(instrumentId, {});
|
|
75
|
+
}
|
|
76
|
+
const assetData = this.assets.get(instrumentId);
|
|
77
|
+
|
|
78
|
+
const leverage = pos.Leverage || 1;
|
|
79
|
+
const key = this._initLeverage(assetData, leverage);
|
|
80
|
+
|
|
81
|
+
const distance = Math.abs(tp_rate - current_price);
|
|
82
|
+
const distance_pct = (distance / current_price);
|
|
83
|
+
|
|
84
|
+
assetData[key].sum_dist += distance_pct;
|
|
85
|
+
assetData[key].count++;
|
|
45
86
|
}
|
|
46
87
|
}
|
|
47
88
|
|
|
@@ -49,18 +90,16 @@ class DistanceToTakeProfitPerLeverage {
|
|
|
49
90
|
if (!this.mappings) {
|
|
50
91
|
this.mappings = await loadInstrumentMappings();
|
|
51
92
|
}
|
|
93
|
+
|
|
52
94
|
const result = {};
|
|
53
|
-
for (const instrumentId
|
|
54
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId
|
|
95
|
+
for (const [instrumentId, leverageData] of this.assets.entries()) {
|
|
96
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
|
|
55
97
|
result[ticker] = {};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
98
|
+
|
|
99
|
+
for (const leverageKey in leverageData) {
|
|
100
|
+
const data = leverageData[leverageKey];
|
|
59
101
|
if (data.count > 0) {
|
|
60
|
-
result[ticker][
|
|
61
|
-
average_distance_percent: data.distance_sum_percent / data.count,
|
|
62
|
-
count: data.count
|
|
63
|
-
};
|
|
102
|
+
result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
|
|
64
103
|
}
|
|
65
104
|
}
|
|
66
105
|
}
|
|
@@ -68,9 +107,9 @@ class DistanceToTakeProfitPerLeverage {
|
|
|
68
107
|
}
|
|
69
108
|
|
|
70
109
|
reset() {
|
|
71
|
-
this.
|
|
110
|
+
this.assets.clear();
|
|
72
111
|
this.mappings = null;
|
|
73
112
|
}
|
|
74
113
|
}
|
|
75
114
|
|
|
76
|
-
module.exports = DistanceToTakeProfitPerLeverage;
|
|
115
|
+
module.exports = DistanceToTakeProfitPerLeverage;
|
|
@@ -1,48 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator metric.
|
|
3
|
+
*
|
|
4
|
+
* This metric answers: "For each asset and leverage level,
|
|
5
|
+
* what is the average percentage distance from the *entry price*
|
|
6
|
+
* to the stop loss?" (This defines the intended risk).
|
|
5
7
|
*/
|
|
6
8
|
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
9
|
|
|
8
|
-
class
|
|
10
|
+
class EntryDistanceToSlPerLeverage {
|
|
9
11
|
constructor() {
|
|
10
|
-
|
|
12
|
+
// { [instrumentId]: { [leverage]: { sum_dist: 0, count: 0 } } }
|
|
13
|
+
this.assets = new Map();
|
|
11
14
|
this.mappings = null;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Defines the output schema for this calculation.
|
|
19
|
+
* @returns {object} JSON Schema object
|
|
20
|
+
*/
|
|
21
|
+
static getSchema() {
|
|
22
|
+
const leverageSchema = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"description": "Average entry-to-SL distance grouped by leverage.",
|
|
25
|
+
"patternProperties": {
|
|
26
|
+
// Leverage level, e.g., "1x", "5x", "10x"
|
|
27
|
+
"^[0-9]+x$": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"description": "The average percentage distance from entry price to Stop Loss for this leverage level."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": { "type": "number" }
|
|
33
|
+
};
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
return {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"description": "Calculates avg % distance from entry price to SL, bucketed by asset and leverage.",
|
|
38
|
+
"patternProperties": {
|
|
39
|
+
"^.*$": leverageSchema // Ticker
|
|
40
|
+
},
|
|
41
|
+
"additionalProperties": leverageSchema
|
|
42
|
+
};
|
|
43
|
+
}
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
_initLeverage(assetData, leverage) {
|
|
46
|
+
const key = `${leverage}x`;
|
|
47
|
+
if (!assetData[key]) {
|
|
48
|
+
assetData[key] = { sum_dist: 0, count: 0 };
|
|
49
|
+
}
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
33
52
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
process(portfolioData) {
|
|
54
|
+
if (portfolioData?.context?.userType !== 'speculator') {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const positions = portfolioData.PublicPositions;
|
|
59
|
+
if (!positions || !Array.isArray(positions)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const pos of positions) {
|
|
64
|
+
const instrumentId = pos.InstrumentID;
|
|
65
|
+
const sl_rate = pos.StopLossRate || 0;
|
|
66
|
+
const open_rate = pos.OpenRate || 0;
|
|
67
|
+
|
|
68
|
+
if (!instrumentId || sl_rate === 0 || open_rate === 0) {
|
|
69
|
+
continue; // Skip positions without SL or open rate
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!this.assets.has(instrumentId)) {
|
|
73
|
+
this.assets.set(instrumentId, {});
|
|
45
74
|
}
|
|
75
|
+
const assetData = this.assets.get(instrumentId);
|
|
76
|
+
|
|
77
|
+
const leverage = pos.Leverage || 1;
|
|
78
|
+
const key = this._initLeverage(assetData, leverage);
|
|
79
|
+
|
|
80
|
+
const distance = Math.abs(open_rate - sl_rate);
|
|
81
|
+
const distance_pct = (distance / open_rate);
|
|
82
|
+
|
|
83
|
+
assetData[key].sum_dist += distance_pct;
|
|
84
|
+
assetData[key].count++;
|
|
46
85
|
}
|
|
47
86
|
}
|
|
48
87
|
|
|
@@ -52,17 +91,14 @@ class EntryDistanceToStopLossPerLeverage {
|
|
|
52
91
|
}
|
|
53
92
|
|
|
54
93
|
const result = {};
|
|
55
|
-
for (const instrumentId
|
|
56
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId
|
|
94
|
+
for (const [instrumentId, leverageData] of this.assets.entries()) {
|
|
95
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
|
|
57
96
|
result[ticker] = {};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
97
|
+
|
|
98
|
+
for (const leverageKey in leverageData) {
|
|
99
|
+
const data = leverageData[leverageKey];
|
|
61
100
|
if (data.count > 0) {
|
|
62
|
-
result[ticker][
|
|
63
|
-
average_distance_percent: data.distance_sum_percent / data.count,
|
|
64
|
-
count: data.count
|
|
65
|
-
};
|
|
101
|
+
result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
|
|
66
102
|
}
|
|
67
103
|
}
|
|
68
104
|
}
|
|
@@ -70,9 +106,9 @@ class EntryDistanceToStopLossPerLeverage {
|
|
|
70
106
|
}
|
|
71
107
|
|
|
72
108
|
reset() {
|
|
73
|
-
this.
|
|
109
|
+
this.assets.clear();
|
|
74
110
|
this.mappings = null;
|
|
75
111
|
}
|
|
76
112
|
}
|
|
77
113
|
|
|
78
|
-
module.exports =
|
|
114
|
+
module.exports = EntryDistanceToSlPerLeverage;
|
|
@@ -1,48 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator metric.
|
|
3
|
+
*
|
|
4
|
+
* This metric answers: "For each asset and leverage level,
|
|
5
|
+
* what is the average percentage distance from the *entry price*
|
|
6
|
+
* to the take profit?" (This defines the intended reward).
|
|
5
7
|
*/
|
|
6
8
|
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
7
9
|
|
|
8
|
-
class
|
|
10
|
+
class EntryDistanceToTpPerLeverage {
|
|
9
11
|
constructor() {
|
|
10
|
-
|
|
12
|
+
// { [instrumentId]: { [leverage]: { sum_dist: 0, count: 0 } } }
|
|
13
|
+
this.assets = new Map();
|
|
11
14
|
this.mappings = null;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Defines the output schema for this calculation.
|
|
19
|
+
* @returns {object} JSON Schema object
|
|
20
|
+
*/
|
|
21
|
+
static getSchema() {
|
|
22
|
+
const leverageSchema = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"description": "Average entry-to-TP distance grouped by leverage.",
|
|
25
|
+
"patternProperties": {
|
|
26
|
+
// Leverage level, e.g., "1x", "5x", "10x"
|
|
27
|
+
"^[0-9]+x$": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"description": "The average percentage distance from entry price to Take Profit for this leverage level."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": { "type": "number" }
|
|
33
|
+
};
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
return {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"description": "Calculates avg % distance from entry price to TP, bucketed by asset and leverage.",
|
|
38
|
+
"patternProperties": {
|
|
39
|
+
"^.*$": leverageSchema // Ticker
|
|
40
|
+
},
|
|
41
|
+
"additionalProperties": leverageSchema
|
|
42
|
+
};
|
|
43
|
+
}
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
_initLeverage(assetData, leverage) {
|
|
46
|
+
const key = `${leverage}x`;
|
|
47
|
+
if (!assetData[key]) {
|
|
48
|
+
assetData[key] = { sum_dist: 0, count: 0 };
|
|
49
|
+
}
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
33
52
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
process(portfolioData) {
|
|
54
|
+
if (portfolioData?.context?.userType !== 'speculator') {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const positions = portfolioData.PublicPositions;
|
|
59
|
+
if (!positions || !Array.isArray(positions)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const pos of positions) {
|
|
64
|
+
const instrumentId = pos.InstrumentID;
|
|
65
|
+
const tp_rate = pos.TakeProfitRate || 0;
|
|
66
|
+
const open_rate = pos.OpenRate || 0;
|
|
67
|
+
|
|
68
|
+
if (!instrumentId || tp_rate === 0 || open_rate === 0) {
|
|
69
|
+
continue; // Skip positions without TP or open rate
|
|
45
70
|
}
|
|
71
|
+
|
|
72
|
+
if (!this.assets.has(instrumentId)) {
|
|
73
|
+
this.assets.set(instrumentId, {});
|
|
74
|
+
}
|
|
75
|
+
const assetData = this.assets.get(instrumentId);
|
|
76
|
+
|
|
77
|
+
const leverage = pos.Leverage || 1;
|
|
78
|
+
const key = this._initLeverage(assetData, leverage);
|
|
79
|
+
|
|
80
|
+
const distance = Math.abs(tp_rate - open_rate);
|
|
81
|
+
const distance_pct = (distance / open_rate);
|
|
82
|
+
|
|
83
|
+
assetData[key].sum_dist += distance_pct;
|
|
84
|
+
assetData[key].count++;
|
|
46
85
|
}
|
|
47
86
|
}
|
|
48
87
|
|
|
@@ -50,18 +89,16 @@ class EntryDistanceToTakeProfitPerLeverage {
|
|
|
50
89
|
if (!this.mappings) {
|
|
51
90
|
this.mappings = await loadInstrumentMappings();
|
|
52
91
|
}
|
|
92
|
+
|
|
53
93
|
const result = {};
|
|
54
|
-
for (const instrumentId
|
|
55
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId
|
|
94
|
+
for (const [instrumentId, leverageData] of this.assets.entries()) {
|
|
95
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || `id_${instrumentId}`;
|
|
56
96
|
result[ticker] = {};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
97
|
+
|
|
98
|
+
for (const leverageKey in leverageData) {
|
|
99
|
+
const data = leverageData[leverageKey];
|
|
60
100
|
if (data.count > 0) {
|
|
61
|
-
result[ticker][
|
|
62
|
-
average_distance_percent: data.distance_sum_percent / data.count,
|
|
63
|
-
count: data.count
|
|
64
|
-
};
|
|
101
|
+
result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
|
|
65
102
|
}
|
|
66
103
|
}
|
|
67
104
|
}
|
|
@@ -69,9 +106,9 @@ class EntryDistanceToTakeProfitPerLeverage {
|
|
|
69
106
|
}
|
|
70
107
|
|
|
71
108
|
reset() {
|
|
72
|
-
this.
|
|
109
|
+
this.assets.clear();
|
|
73
110
|
this.mappings = null;
|
|
74
111
|
}
|
|
75
112
|
}
|
|
76
113
|
|
|
77
|
-
module.exports =
|
|
114
|
+
module.exports = EntryDistanceToTpPerLeverage;
|