aiden-shared-calculations-unified 1.0.82 → 1.0.84
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 -14
- 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 +9 -9
- package/README.MD +0 -78
|
@@ -1,138 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator SL entry distance.
|
|
3
|
+
* --- FIX ---
|
|
4
|
+
* - Rewritten logic to calculate *entry* SL distance from raw schema fields
|
|
5
|
+
* (StopLossRate, OpenRate) instead of the non-existent
|
|
6
|
+
* 'PctToStopLossAtEntry' field.
|
|
7
|
+
* - Updated process signature to match worker.
|
|
7
8
|
*/
|
|
8
|
-
const
|
|
9
|
+
const BUCKETS = [1, 2, 5, 10, 20, 30]; // Leverage buckets
|
|
9
10
|
|
|
10
|
-
class
|
|
11
|
+
class SpeculatorEntryDistanceToSLPerLeverage {
|
|
11
12
|
constructor() {
|
|
12
|
-
|
|
13
|
-
this.
|
|
14
|
-
|
|
13
|
+
this.buckets = new Map();
|
|
14
|
+
this._initBuckets();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_initBuckets() {
|
|
18
|
+
this.buckets.clear();
|
|
19
|
+
for (const b of BUCKETS) {
|
|
20
|
+
this.buckets.set(b, { sum: 0, count: 0 });
|
|
21
|
+
}
|
|
22
|
+
this.buckets.set('other', { sum: 0, count: 0 });
|
|
15
23
|
}
|
|
16
24
|
|
|
17
|
-
// --- NEW ---
|
|
18
|
-
/**
|
|
19
|
-
* Statically defines all metadata for the manifest builder.
|
|
20
|
-
*/
|
|
21
25
|
static getMetadata() {
|
|
22
26
|
return {
|
|
23
27
|
type: 'standard',
|
|
24
|
-
rootDataDependencies: ['portfolio'],
|
|
25
|
-
isHistorical: false,
|
|
26
|
-
userType: 'speculator',
|
|
28
|
+
rootDataDependencies: ['portfolio'],
|
|
29
|
+
isHistorical: false,
|
|
30
|
+
userType: 'speculator',
|
|
27
31
|
category: 'core_speculator'
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
// --- NEW ---
|
|
32
|
-
/**
|
|
33
|
-
* Statically declare dependencies.
|
|
34
|
-
*/
|
|
35
35
|
static getDependencies() {
|
|
36
|
-
return [];
|
|
36
|
+
return [];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
* Defines the output schema for this calculation.
|
|
41
|
-
* @returns {object} JSON Schema object
|
|
42
|
-
*/
|
|
43
39
|
static getSchema() {
|
|
44
|
-
const
|
|
40
|
+
const bucketSchema = {
|
|
45
41
|
"type": "object",
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"type": "number",
|
|
51
|
-
"description": "The average percentage distance from entry price to Stop Loss for this leverage level."
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"additionalProperties": { "type": "number" }
|
|
42
|
+
"properties": {
|
|
43
|
+
"avg_entry_distance_pct": { "type": "number" },
|
|
44
|
+
"position_count": { "type": "number" }
|
|
45
|
+
}
|
|
55
46
|
};
|
|
56
47
|
|
|
57
48
|
return {
|
|
58
49
|
"type": "object",
|
|
59
|
-
"description": "Calculates
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
"description": "Calculates the average *entry* distance to stop-loss, bucketed by leverage.",
|
|
51
|
+
"properties": {
|
|
52
|
+
"x1": bucketSchema,
|
|
53
|
+
"x2": bucketSchema,
|
|
54
|
+
"x5": bucketSchema,
|
|
55
|
+
"x10": bucketSchema,
|
|
56
|
+
"x20": bucketSchema,
|
|
57
|
+
"x30": bucketSchema,
|
|
58
|
+
"other": bucketSchema
|
|
59
|
+
}
|
|
64
60
|
};
|
|
65
61
|
}
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
assetData[key] = { sum_dist: 0, count: 0 };
|
|
63
|
+
_getBucket(leverage) {
|
|
64
|
+
if (this.buckets.has(leverage)) {
|
|
65
|
+
return this.buckets.get(leverage);
|
|
71
66
|
}
|
|
72
|
-
return
|
|
67
|
+
return this.buckets.get('other');
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
// ---
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (portfolioData?.context?.userType !== 'speculator') {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const positions = portfolioData.PublicPositions;
|
|
70
|
+
// --- THIS IS THE FIX ---
|
|
71
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
72
|
+
const positions = todayPortfolio.PublicPositions;
|
|
83
73
|
if (!positions || !Array.isArray(positions)) {
|
|
84
74
|
return;
|
|
85
75
|
}
|
|
86
76
|
|
|
87
77
|
for (const pos of positions) {
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
if (!
|
|
93
|
-
continue;
|
|
78
|
+
const leverage = pos.Leverage;
|
|
79
|
+
const slRate = pos.StopLossRate;
|
|
80
|
+
const openRate = pos.OpenRate;
|
|
81
|
+
|
|
82
|
+
if (!leverage || !slRate || slRate <= 0 || !openRate || openRate <= 0) {
|
|
83
|
+
continue;
|
|
94
84
|
}
|
|
95
85
|
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
let pctToSL = 0;
|
|
87
|
+
if (pos.IsBuy) {
|
|
88
|
+
// Long: (Open - SL) / Open
|
|
89
|
+
pctToSL = (openRate - slRate) / openRate;
|
|
90
|
+
} else {
|
|
91
|
+
// Short: (SL - Open) / Open
|
|
92
|
+
pctToSL = (slRate - openRate) / openRate;
|
|
98
93
|
}
|
|
99
|
-
const assetData = this.assets.get(instrumentId);
|
|
100
|
-
|
|
101
|
-
const leverage = pos.Leverage || 1;
|
|
102
|
-
const key = this._initLeverage(assetData, leverage);
|
|
103
|
-
|
|
104
|
-
const distance = Math.abs(open_rate - sl_rate);
|
|
105
|
-
const distance_pct = (distance / open_rate);
|
|
106
|
-
|
|
107
|
-
assetData[key].sum_dist += distance_pct;
|
|
108
|
-
assetData[key].count++;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
94
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
95
|
+
if (pctToSL <= 0) {
|
|
96
|
+
continue; // SL is at or beyond open price, invalid
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const bucket = this._getBucket(leverage);
|
|
100
|
+
bucket.sum += (pctToSL * 100); // Convert to percentage
|
|
101
|
+
bucket.count++;
|
|
115
102
|
}
|
|
103
|
+
}
|
|
104
|
+
// --- END FIX ---
|
|
116
105
|
|
|
106
|
+
getResult() {
|
|
117
107
|
const result = {};
|
|
118
|
-
for (const [
|
|
119
|
-
const
|
|
120
|
-
result[
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (data.count > 0) {
|
|
125
|
-
result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
108
|
+
for (const [key, data] of this.buckets.entries()) {
|
|
109
|
+
const label = (key === 'other') ? 'other' : `x${key}`;
|
|
110
|
+
result[label] = {
|
|
111
|
+
avg_entry_distance_pct: (data.count > 0) ? (data.sum / data.count) : 0,
|
|
112
|
+
position_count: data.count
|
|
113
|
+
};
|
|
128
114
|
}
|
|
129
115
|
return result;
|
|
130
116
|
}
|
|
131
117
|
|
|
132
118
|
reset() {
|
|
133
|
-
this.
|
|
134
|
-
this.mappings = null;
|
|
119
|
+
this._initBuckets();
|
|
135
120
|
}
|
|
136
121
|
}
|
|
137
122
|
|
|
138
|
-
module.exports =
|
|
123
|
+
module.exports = SpeculatorEntryDistanceToSLPerLeverage;
|
|
@@ -1,138 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator TP entry distance.
|
|
3
|
+
* --- FIX ---
|
|
4
|
+
* - Rewritten logic to calculate *entry* TP distance from raw schema fields
|
|
5
|
+
* (TakeProfitRate, OpenRate) instead of the non-existent
|
|
6
|
+
* 'PctToTakeProfitAtEntry' field.
|
|
7
|
+
* - Updated process signature to match worker.
|
|
7
8
|
*/
|
|
8
|
-
const
|
|
9
|
+
const BUCKETS = [1, 2, 5, 10, 20, 30]; // Leverage buckets
|
|
9
10
|
|
|
10
|
-
class
|
|
11
|
+
class SpeculatorEntryDistanceToTPPerLeverage {
|
|
11
12
|
constructor() {
|
|
12
|
-
|
|
13
|
-
this.
|
|
14
|
-
|
|
13
|
+
this.buckets = new Map();
|
|
14
|
+
this._initBuckets();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_initBuckets() {
|
|
18
|
+
this.buckets.clear();
|
|
19
|
+
for (const b of BUCKETS) {
|
|
20
|
+
this.buckets.set(b, { sum: 0, count: 0 });
|
|
21
|
+
}
|
|
22
|
+
this.buckets.set('other', { sum: 0, count: 0 });
|
|
15
23
|
}
|
|
16
24
|
|
|
17
|
-
// --- NEW ---
|
|
18
|
-
/**
|
|
19
|
-
* Statically defines all metadata for the manifest builder.
|
|
20
|
-
*/
|
|
21
25
|
static getMetadata() {
|
|
22
26
|
return {
|
|
23
27
|
type: 'standard',
|
|
24
|
-
rootDataDependencies: ['portfolio'],
|
|
28
|
+
rootDataDependencies: ['portfolio'],
|
|
25
29
|
isHistorical: false,
|
|
26
|
-
userType: 'speculator',
|
|
30
|
+
userType: 'speculator',
|
|
27
31
|
category: 'core_speculator'
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
// --- NEW ---
|
|
32
|
-
/**
|
|
33
|
-
* Statically declare dependencies.
|
|
34
|
-
*/
|
|
35
35
|
static getDependencies() {
|
|
36
|
-
return [];
|
|
36
|
+
return [];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
* Defines the output schema for this calculation.
|
|
41
|
-
* @returns {object} JSON Schema object
|
|
42
|
-
*/
|
|
43
39
|
static getSchema() {
|
|
44
|
-
const
|
|
40
|
+
const bucketSchema = {
|
|
45
41
|
"type": "object",
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"type": "number",
|
|
51
|
-
"description": "The average percentage distance from entry price to Take Profit for this leverage level."
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"additionalProperties": { "type": "number" }
|
|
42
|
+
"properties": {
|
|
43
|
+
"avg_entry_distance_pct": { "type": "number" },
|
|
44
|
+
"position_count": { "type": "number" }
|
|
45
|
+
}
|
|
55
46
|
};
|
|
56
47
|
|
|
57
48
|
return {
|
|
58
49
|
"type": "object",
|
|
59
|
-
"description": "Calculates
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
"description": "Calculates the average *entry* distance to take-profit, bucketed by leverage.",
|
|
51
|
+
"properties": {
|
|
52
|
+
"x1": bucketSchema,
|
|
53
|
+
"x2": bucketSchema,
|
|
54
|
+
"x5": bucketSchema,
|
|
55
|
+
"x10": bucketSchema,
|
|
56
|
+
"x20": bucketSchema,
|
|
57
|
+
"x30": bucketSchema,
|
|
58
|
+
"other": bucketSchema
|
|
59
|
+
}
|
|
64
60
|
};
|
|
65
61
|
}
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
assetData[key] = { sum_dist: 0, count: 0 };
|
|
63
|
+
_getBucket(leverage) {
|
|
64
|
+
if (this.buckets.has(leverage)) {
|
|
65
|
+
return this.buckets.get(leverage);
|
|
71
66
|
}
|
|
72
|
-
return
|
|
67
|
+
return this.buckets.get('other');
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
// ---
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (portfolioData?.context?.userType !== 'speculator') {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const positions = portfolioData.PublicPositions;
|
|
70
|
+
// --- THIS IS THE FIX ---
|
|
71
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
72
|
+
const positions = todayPortfolio.PublicPositions;
|
|
83
73
|
if (!positions || !Array.isArray(positions)) {
|
|
84
74
|
return;
|
|
85
75
|
}
|
|
86
76
|
|
|
87
77
|
for (const pos of positions) {
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
if (!
|
|
93
|
-
continue;
|
|
78
|
+
const leverage = pos.Leverage;
|
|
79
|
+
const tpRate = pos.TakeProfitRate;
|
|
80
|
+
const openRate = pos.OpenRate;
|
|
81
|
+
|
|
82
|
+
if (!leverage || !tpRate || tpRate <= 0 || !openRate || openRate <= 0) {
|
|
83
|
+
continue;
|
|
94
84
|
}
|
|
95
85
|
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
let pctToTP = 0;
|
|
87
|
+
if (pos.IsBuy) {
|
|
88
|
+
// Long: (TP - Open) / Open
|
|
89
|
+
pctToTP = (tpRate - openRate) / openRate;
|
|
90
|
+
} else {
|
|
91
|
+
// Short: (Open - TP) / Open
|
|
92
|
+
pctToTP = (openRate - tpRate) / openRate;
|
|
98
93
|
}
|
|
99
|
-
const assetData = this.assets.get(instrumentId);
|
|
100
|
-
|
|
101
|
-
const leverage = pos.Leverage || 1;
|
|
102
|
-
const key = this._initLeverage(assetData, leverage);
|
|
103
|
-
|
|
104
|
-
const distance = Math.abs(tp_rate - open_rate);
|
|
105
|
-
const distance_pct = (distance / open_rate);
|
|
106
|
-
|
|
107
|
-
assetData[key].sum_dist += distance_pct;
|
|
108
|
-
assetData[key].count++;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
94
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
95
|
+
if (pctToTP <= 0) {
|
|
96
|
+
continue; // TP is at or below open price, invalid
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const bucket = this._getBucket(leverage);
|
|
100
|
+
bucket.sum += (pctToTP * 100); // Convert to percentage
|
|
101
|
+
bucket.count++;
|
|
115
102
|
}
|
|
103
|
+
}
|
|
104
|
+
// --- END FIX ---
|
|
116
105
|
|
|
106
|
+
getResult() {
|
|
117
107
|
const result = {};
|
|
118
|
-
for (const [
|
|
119
|
-
const
|
|
120
|
-
result[
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (data.count > 0) {
|
|
125
|
-
result[ticker][leverageKey] = (data.sum_dist / data.count) * 100;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
108
|
+
for (const [key, data] of this.buckets.entries()) {
|
|
109
|
+
const label = (key === 'other') ? 'other' : `x${key}`;
|
|
110
|
+
result[label] = {
|
|
111
|
+
avg_entry_distance_pct: (data.count > 0) ? (data.sum / data.count) : 0,
|
|
112
|
+
position_count: data.count
|
|
113
|
+
};
|
|
128
114
|
}
|
|
129
115
|
return result;
|
|
130
116
|
}
|
|
131
117
|
|
|
132
118
|
reset() {
|
|
133
|
-
this.
|
|
134
|
-
this.mappings = null;
|
|
119
|
+
this._initBuckets();
|
|
135
120
|
}
|
|
136
121
|
}
|
|
137
122
|
|
|
138
|
-
module.exports =
|
|
123
|
+
module.exports = SpeculatorEntryDistanceToTPPerLeverage;
|
|
@@ -1,44 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator leverage.
|
|
3
3
|
*
|
|
4
|
-
* This metric answers: "For each asset, what is the
|
|
5
|
-
*
|
|
4
|
+
* This metric answers: "For each asset, what is the average
|
|
5
|
+
* leverage used by speculators?"
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class SpeculatorLeveragePerAsset {
|
|
10
10
|
constructor() {
|
|
11
|
-
// { [instrumentId]: {
|
|
11
|
+
// { [instrumentId]: { sum: 0, count: 0 } }
|
|
12
12
|
this.assets = new Map();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Defines the output schema for this calculation.
|
|
18
|
-
* @returns {object} JSON Schema object
|
|
19
|
-
*/
|
|
20
|
-
static getSchema() {
|
|
21
|
-
const leverageSchema = {
|
|
22
|
-
"type": "object",
|
|
23
|
-
"description": "Distribution of leverage levels for this asset.",
|
|
24
|
-
"patternProperties": {
|
|
25
|
-
// Leverage level, e.g., "1x", "5x"
|
|
26
|
-
"^[0-9]+x$": {
|
|
27
|
-
"type": "number",
|
|
28
|
-
"description": "Count of positions at this leverage."
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"additionalProperties": { "type": "number" }
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
"type": "object",
|
|
36
|
-
"description": "Calculates the distribution of leverage levels used by speculators for each asset.",
|
|
37
|
-
"patternProperties": {
|
|
38
|
-
"^.*$": leverageSchema // Ticker
|
|
39
|
-
},
|
|
40
|
-
"additionalProperties": leverageSchema
|
|
41
|
-
};
|
|
13
|
+
// --- STANDARD 0: RENAMED ---
|
|
14
|
+
this.tickerMap = null;
|
|
42
15
|
}
|
|
43
16
|
|
|
44
17
|
/**
|
|
@@ -49,7 +22,7 @@ class LeveragePerAsset {
|
|
|
49
22
|
type: 'standard',
|
|
50
23
|
rootDataDependencies: ['portfolio'],
|
|
51
24
|
isHistorical: false,
|
|
52
|
-
userType: 'speculator',
|
|
25
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
53
26
|
category: 'core_speculator'
|
|
54
27
|
};
|
|
55
28
|
}
|
|
@@ -61,56 +34,88 @@ class LeveragePerAsset {
|
|
|
61
34
|
return [];
|
|
62
35
|
}
|
|
63
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Defines the output schema for this calculation.
|
|
39
|
+
*/
|
|
40
|
+
static getSchema() {
|
|
41
|
+
const tickerSchema = {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"avg_leverage": { "type": "number" },
|
|
45
|
+
"position_count": { "type": "number" }
|
|
46
|
+
},
|
|
47
|
+
"required": ["avg_leverage", "position_count"]
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"description": "Calculates the average leverage used by speculators for each asset.",
|
|
53
|
+
"patternProperties": { "^.*$": tickerSchema },
|
|
54
|
+
"additionalProperties": tickerSchema
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
64
58
|
_initAsset(instrumentId) {
|
|
65
59
|
if (!this.assets.has(instrumentId)) {
|
|
66
|
-
this.assets.set(instrumentId, {});
|
|
60
|
+
this.assets.set(instrumentId, { sum: 0, count: 0 });
|
|
67
61
|
}
|
|
68
62
|
}
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
// --- STANDARD 0: UPDATED SIGNATURE ---
|
|
65
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
66
|
+
// --- STANDARD 0: ADDED ---
|
|
67
|
+
if (!this.tickerMap) {
|
|
68
|
+
this.tickerMap = context.instrumentToTicker;
|
|
73
69
|
}
|
|
74
|
-
|
|
75
|
-
const positions =
|
|
70
|
+
|
|
71
|
+
const positions = todayPortfolio.PublicPositions;
|
|
76
72
|
if (!positions || !Array.isArray(positions)) {
|
|
77
73
|
return;
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
for (const pos of positions) {
|
|
81
77
|
const instrumentId = pos.InstrumentID;
|
|
82
|
-
|
|
78
|
+
const leverage = pos.Leverage;
|
|
79
|
+
|
|
80
|
+
if (!instrumentId || !leverage || leverage <= 1) {
|
|
81
|
+
continue; // Only care about leveraged positions
|
|
82
|
+
}
|
|
83
83
|
|
|
84
84
|
this._initAsset(instrumentId);
|
|
85
85
|
const assetData = this.assets.get(instrumentId);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const key = `${leverage}x`;
|
|
89
|
-
|
|
90
|
-
if (!assetData[key]) {
|
|
91
|
-
assetData[key] = 0;
|
|
92
|
-
}
|
|
93
|
-
assetData[key]++;
|
|
86
|
+
assetData.sum += leverage;
|
|
87
|
+
assetData.count++;
|
|
94
88
|
}
|
|
95
89
|
}
|
|
96
90
|
|
|
97
91
|
async getResult() {
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
93
|
+
|
|
94
|
+
// Failsafe check
|
|
95
|
+
if (!this.tickerMap) {
|
|
96
|
+
return {}; // process() must run first
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
const result = {};
|
|
103
100
|
for (const [instrumentId, data] of this.assets.entries()) {
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
102
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
103
|
+
|
|
104
|
+
if (data.count > 0) {
|
|
105
|
+
result[ticker] = {
|
|
106
|
+
avg_leverage: data.sum / data.count,
|
|
107
|
+
position_count: data.count
|
|
108
|
+
};
|
|
109
|
+
}
|
|
106
110
|
}
|
|
107
111
|
return result;
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
reset() {
|
|
111
115
|
this.assets.clear();
|
|
112
|
-
|
|
116
|
+
// --- STANDARD 0: RENAMED ---
|
|
117
|
+
this.tickerMap = null;
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
module.exports =
|
|
121
|
+
module.exports = SpeculatorLeveragePerAsset;
|