aiden-shared-calculations-unified 1.0.83 → 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 -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,144 +1,124 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator SL distance.
|
|
3
|
+
* --- FIX ---
|
|
4
|
+
* - Rewritten logic to calculate SL distance from raw schema fields
|
|
5
|
+
* (StopLossRate, CurrentRate) instead of 'PctToStopLoss'.
|
|
6
|
+
* - Updated process signature to match worker.
|
|
7
7
|
*/
|
|
8
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
9
8
|
|
|
10
|
-
class
|
|
9
|
+
class SpeculatorSLDistanceByTickerShortLong {
|
|
11
10
|
constructor() {
|
|
12
|
-
// { [instrumentId]: { long_pct: [], long_val: [], short_pct: [], short_val: [] } }
|
|
13
11
|
this.assets = new Map();
|
|
14
|
-
this.
|
|
12
|
+
this.tickerMap = null;
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
// --- NEW ---
|
|
18
|
-
/**
|
|
19
|
-
* Statically defines all metadata for the manifest builder.
|
|
20
|
-
*/
|
|
21
15
|
static getMetadata() {
|
|
22
16
|
return {
|
|
23
17
|
type: 'standard',
|
|
24
18
|
rootDataDependencies: ['portfolio'],
|
|
25
19
|
isHistorical: false,
|
|
26
|
-
userType: 'speculator',
|
|
20
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
27
21
|
category: 'core_speculator'
|
|
28
22
|
};
|
|
29
23
|
}
|
|
30
24
|
|
|
31
|
-
// --- NEW ---
|
|
32
|
-
/**
|
|
33
|
-
* Statically declare dependencies.
|
|
34
|
-
*/
|
|
35
25
|
static getDependencies() {
|
|
36
26
|
return [];
|
|
37
27
|
}
|
|
38
28
|
|
|
39
|
-
/**
|
|
40
|
-
* Defines the output schema for this calculation.
|
|
41
|
-
* @returns {object} JSON Schema object
|
|
42
|
-
*/
|
|
43
29
|
static getSchema() {
|
|
44
30
|
const tickerSchema = {
|
|
45
31
|
"type": "object",
|
|
46
32
|
"properties": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
"short_count": { "type": "number" }
|
|
53
|
-
},
|
|
54
|
-
"required": ["long_avg_dist_pct", "long_avg_dist_val", "long_count", "short_avg_dist_pct", "short_avg_dist_val", "short_count"]
|
|
33
|
+
"long_avg_distance_pct": { "type": "number" },
|
|
34
|
+
"long_position_count": { "type": "number" },
|
|
35
|
+
"short_avg_distance_pct": { "type": "number" },
|
|
36
|
+
"short_position_count": { "type": "number" }
|
|
37
|
+
}
|
|
55
38
|
};
|
|
56
39
|
|
|
57
40
|
return {
|
|
58
41
|
"type": "object",
|
|
59
|
-
"description": "Calculates
|
|
60
|
-
"patternProperties": {
|
|
61
|
-
"^.*$": tickerSchema // Ticker
|
|
62
|
-
},
|
|
42
|
+
"description": "Calculates the average distance to SL, broken down by long/short, per asset.",
|
|
43
|
+
"patternProperties": { "^.*$": tickerSchema },
|
|
63
44
|
"additionalProperties": tickerSchema
|
|
64
45
|
};
|
|
65
46
|
}
|
|
66
47
|
|
|
67
48
|
_initAsset(instrumentId) {
|
|
68
49
|
if (!this.assets.has(instrumentId)) {
|
|
69
|
-
this.assets.set(instrumentId, {
|
|
70
|
-
long_pct: [], long_val: [], short_pct: [], short_val: []
|
|
71
|
-
});
|
|
50
|
+
this.assets.set(instrumentId, { long_sum: 0, long_count: 0, short_sum: 0, short_count: 0 });
|
|
72
51
|
}
|
|
73
52
|
}
|
|
74
|
-
|
|
75
|
-
_avg(arr) {
|
|
76
|
-
if (arr.length === 0) return 0;
|
|
77
|
-
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
78
|
-
}
|
|
79
53
|
|
|
80
|
-
// ---
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return;
|
|
54
|
+
// --- THIS IS THE FIX ---
|
|
55
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
56
|
+
if (!this.tickerMap) {
|
|
57
|
+
this.tickerMap = context.instrumentToTicker;
|
|
85
58
|
}
|
|
86
|
-
|
|
87
|
-
const positions =
|
|
59
|
+
|
|
60
|
+
const positions = todayPortfolio.PublicPositions;
|
|
88
61
|
if (!positions || !Array.isArray(positions)) {
|
|
89
62
|
return;
|
|
90
63
|
}
|
|
91
64
|
|
|
92
65
|
for (const pos of positions) {
|
|
93
66
|
const instrumentId = pos.InstrumentID;
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
if (!instrumentId ||
|
|
98
|
-
continue;
|
|
67
|
+
const slRate = pos.StopLossRate;
|
|
68
|
+
const currentRate = pos.CurrentRate;
|
|
69
|
+
|
|
70
|
+
if (!instrumentId || !slRate || slRate <= 0 || !currentRate || currentRate <= 0) {
|
|
71
|
+
continue; // No SL or invalid data
|
|
99
72
|
}
|
|
100
|
-
|
|
73
|
+
|
|
101
74
|
this._initAsset(instrumentId);
|
|
102
75
|
const assetData = this.assets.get(instrumentId);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const distance_pct = (distance_val / open_rate) * 100;
|
|
106
|
-
|
|
76
|
+
|
|
77
|
+
let pctToSL = 0;
|
|
107
78
|
if (pos.IsBuy) {
|
|
108
|
-
|
|
109
|
-
|
|
79
|
+
// Long: (Current - SL) / Current
|
|
80
|
+
pctToSL = (currentRate - slRate) / currentRate;
|
|
81
|
+
if (pctToSL > 0) {
|
|
82
|
+
assetData.long_sum += (pctToSL * 100);
|
|
83
|
+
assetData.long_count++;
|
|
84
|
+
}
|
|
110
85
|
} else {
|
|
111
|
-
|
|
112
|
-
|
|
86
|
+
// Short: (SL - Current) / Current
|
|
87
|
+
pctToSL = (slRate - currentRate) / currentRate;
|
|
88
|
+
if (pctToSL > 0) {
|
|
89
|
+
assetData.short_sum += (pctToSL * 100);
|
|
90
|
+
assetData.short_count++;
|
|
91
|
+
}
|
|
113
92
|
}
|
|
114
93
|
}
|
|
115
94
|
}
|
|
95
|
+
// --- END FIX ---
|
|
116
96
|
|
|
117
97
|
async getResult() {
|
|
118
|
-
if (!this.
|
|
119
|
-
|
|
98
|
+
if (!this.tickerMap) {
|
|
99
|
+
return {};
|
|
120
100
|
}
|
|
121
101
|
|
|
122
102
|
const result = {};
|
|
123
103
|
for (const [instrumentId, data] of this.assets.entries()) {
|
|
124
|
-
const ticker = this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
104
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
105
|
+
|
|
106
|
+
if (data.long_count > 0 || data.short_count > 0) {
|
|
107
|
+
result[ticker] = {
|
|
108
|
+
long_avg_distance_pct: (data.long_count > 0) ? (data.long_sum / data.long_count) : 0,
|
|
109
|
+
long_position_count: data.long_count,
|
|
110
|
+
short_avg_distance_pct: (data.short_count > 0) ? (data.short_sum / data.short_count) : 0,
|
|
111
|
+
short_position_count: data.short_count
|
|
112
|
+
};
|
|
113
|
+
}
|
|
134
114
|
}
|
|
135
115
|
return result;
|
|
136
116
|
}
|
|
137
117
|
|
|
138
118
|
reset() {
|
|
139
119
|
this.assets.clear();
|
|
140
|
-
this.
|
|
120
|
+
this.tickerMap = null;
|
|
141
121
|
}
|
|
142
122
|
}
|
|
143
123
|
|
|
144
|
-
module.exports =
|
|
124
|
+
module.exports = SpeculatorSLDistanceByTickerShortLong;
|
|
@@ -1,96 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator SL usage.
|
|
3
3
|
*
|
|
4
|
-
* This metric answers: "For each asset, what
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* This metric answers: "For each asset, what percentage
|
|
5
|
+
* of speculators are using a stop-loss?"
|
|
6
|
+
* --- FIX ---
|
|
7
|
+
* - Logic changed to check 'StopLossRate > 0' instead of 'PctToStopLoss >= 0'
|
|
8
|
+
* to match schema.md.
|
|
7
9
|
*/
|
|
8
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
9
10
|
|
|
10
|
-
class
|
|
11
|
+
class SpeculatorStopLossPerAsset {
|
|
11
12
|
constructor() {
|
|
12
|
-
// { [instrumentId]: { sl_rate_sum: 0, sl_dist_sum: 0, sl_set_count: 0, total_count: 0 } }
|
|
13
13
|
this.assets = new Map();
|
|
14
|
-
this.
|
|
14
|
+
this.tickerMap = null;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// --- NEW ---
|
|
18
|
-
/**
|
|
19
|
-
* Statically defines all metadata for the manifest builder.
|
|
20
|
-
*/
|
|
21
17
|
static getMetadata() {
|
|
22
18
|
return {
|
|
23
19
|
type: 'standard',
|
|
24
20
|
rootDataDependencies: ['portfolio'],
|
|
25
21
|
isHistorical: false,
|
|
26
|
-
userType: 'speculator',
|
|
22
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
27
23
|
category: 'core_speculator'
|
|
28
24
|
};
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
// --- NEW ---
|
|
32
|
-
/**
|
|
33
|
-
* Statically declare dependencies.
|
|
34
|
-
*/
|
|
35
27
|
static getDependencies() {
|
|
36
28
|
return [];
|
|
37
29
|
}
|
|
38
30
|
|
|
39
|
-
/**
|
|
40
|
-
* Defines the output schema for this calculation.
|
|
41
|
-
* @returns {object} JSON Schema object
|
|
42
|
-
*/
|
|
43
31
|
static getSchema() {
|
|
44
32
|
const tickerSchema = {
|
|
45
33
|
"type": "object",
|
|
46
34
|
"properties": {
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
"avg_sl_pct_dist": {
|
|
52
|
-
"type": "number",
|
|
53
|
-
"description": "Average SL distance from open price % (for positions with SL)."
|
|
54
|
-
},
|
|
55
|
-
"sl_set_count": { "type": "number" },
|
|
56
|
-
"total_count": { "type": "number" },
|
|
57
|
-
"sl_set_rate_pct": {
|
|
58
|
-
"type": "number",
|
|
59
|
-
"description": "Percentage of positions that have a SL set."
|
|
60
|
-
}
|
|
35
|
+
"sl_usage_pct": { "type": "number" },
|
|
36
|
+
"sl_count": { "type": "number" },
|
|
37
|
+
"total_count": { "type": "number" }
|
|
61
38
|
},
|
|
62
|
-
"required": ["
|
|
39
|
+
"required": ["sl_usage_pct", "sl_count", "total_count"]
|
|
63
40
|
};
|
|
64
41
|
|
|
65
42
|
return {
|
|
66
43
|
"type": "object",
|
|
67
|
-
"description": "Calculates the
|
|
68
|
-
"patternProperties": {
|
|
69
|
-
"^.*$": tickerSchema // Ticker
|
|
70
|
-
},
|
|
44
|
+
"description": "Calculates the percentage of speculators using a stop-loss per asset.",
|
|
45
|
+
"patternProperties": { "^.*$": tickerSchema },
|
|
71
46
|
"additionalProperties": tickerSchema
|
|
72
47
|
};
|
|
73
48
|
}
|
|
74
49
|
|
|
75
50
|
_initAsset(instrumentId) {
|
|
76
51
|
if (!this.assets.has(instrumentId)) {
|
|
77
|
-
this.assets.set(instrumentId, {
|
|
78
|
-
sl_rate_sum: 0,
|
|
79
|
-
sl_dist_sum: 0,
|
|
80
|
-
sl_set_count: 0,
|
|
81
|
-
total_count: 0
|
|
82
|
-
});
|
|
52
|
+
this.assets.set(instrumentId, { with_sl: 0, total: 0 });
|
|
83
53
|
}
|
|
84
54
|
}
|
|
85
55
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (portfolioData?.context?.userType !== 'speculator') {
|
|
90
|
-
return;
|
|
56
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
57
|
+
if (!this.tickerMap) {
|
|
58
|
+
this.tickerMap = context.instrumentToTicker;
|
|
91
59
|
}
|
|
92
|
-
|
|
93
|
-
const positions =
|
|
60
|
+
|
|
61
|
+
const positions = todayPortfolio.PublicPositions;
|
|
94
62
|
if (!positions || !Array.isArray(positions)) {
|
|
95
63
|
return;
|
|
96
64
|
}
|
|
@@ -98,50 +66,45 @@ class StopLossPerAsset {
|
|
|
98
66
|
for (const pos of positions) {
|
|
99
67
|
const instrumentId = pos.InstrumentID;
|
|
100
68
|
if (!instrumentId) continue;
|
|
101
|
-
|
|
69
|
+
|
|
102
70
|
this._initAsset(instrumentId);
|
|
103
71
|
const assetData = this.assets.get(instrumentId);
|
|
104
|
-
assetData.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (open_rate > 0) {
|
|
113
|
-
const distance = Math.abs(open_rate - sl_rate);
|
|
114
|
-
assetData.sl_dist_sum += (distance / open_rate);
|
|
115
|
-
}
|
|
72
|
+
assetData.total++;
|
|
73
|
+
|
|
74
|
+
// --- THIS IS THE FIX ---
|
|
75
|
+
// 'PctToStopLoss' is not in schema.md. 'StopLossRate' is.
|
|
76
|
+
// A rate > 0 indicates an SL is set.
|
|
77
|
+
if (pos.StopLossRate > 0) {
|
|
78
|
+
// --- END FIX ---
|
|
79
|
+
assetData.with_sl++;
|
|
116
80
|
}
|
|
117
81
|
}
|
|
118
82
|
}
|
|
119
83
|
|
|
120
84
|
async getResult() {
|
|
121
|
-
if (!this.
|
|
122
|
-
|
|
85
|
+
if (!this.tickerMap) {
|
|
86
|
+
return {};
|
|
123
87
|
}
|
|
124
88
|
|
|
125
89
|
const result = {};
|
|
126
90
|
for (const [instrumentId, data] of this.assets.entries()) {
|
|
127
|
-
const ticker = this.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
};
|
|
91
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
92
|
+
|
|
93
|
+
if (data.total > 0) {
|
|
94
|
+
result[ticker] = {
|
|
95
|
+
sl_usage_pct: (data.with_sl / data.total) * 100,
|
|
96
|
+
sl_count: data.with_sl,
|
|
97
|
+
total_count: data.total
|
|
98
|
+
};
|
|
99
|
+
}
|
|
137
100
|
}
|
|
138
101
|
return result;
|
|
139
102
|
}
|
|
140
103
|
|
|
141
104
|
reset() {
|
|
142
105
|
this.assets.clear();
|
|
143
|
-
this.
|
|
106
|
+
this.tickerMap = null;
|
|
144
107
|
}
|
|
145
108
|
}
|
|
146
109
|
|
|
147
|
-
module.exports =
|
|
110
|
+
module.exports = SpeculatorStopLossPerAsset;
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator TP usage.
|
|
3
3
|
*
|
|
4
|
-
* This metric answers: "For each asset, what
|
|
5
|
-
*
|
|
6
|
-
* positions that have a take profit set?"
|
|
4
|
+
* This metric answers: "For each asset, what percentage
|
|
5
|
+
* of speculators are using a take-profit?"
|
|
7
6
|
*/
|
|
8
|
-
|
|
7
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
9
8
|
|
|
10
|
-
class
|
|
9
|
+
class SpeculatorTakeProfitPerAsset {
|
|
11
10
|
constructor() {
|
|
12
|
-
// { [instrumentId]: {
|
|
11
|
+
// { [instrumentId]: { with_tp: 0, total: 0 } }
|
|
13
12
|
this.assets = new Map();
|
|
14
|
-
|
|
13
|
+
// --- STANDARD 0: RENAMED ---
|
|
14
|
+
this.tickerMap = null;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// --- NEW ---
|
|
18
17
|
/**
|
|
19
18
|
* Statically defines all metadata for the manifest builder.
|
|
20
19
|
*/
|
|
@@ -23,12 +22,11 @@ class TakeProfitPerAsset {
|
|
|
23
22
|
type: 'standard',
|
|
24
23
|
rootDataDependencies: ['portfolio'],
|
|
25
24
|
isHistorical: false,
|
|
26
|
-
userType: 'speculator',
|
|
25
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
27
26
|
category: 'core_speculator'
|
|
28
27
|
};
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
// --- NEW ---
|
|
32
30
|
/**
|
|
33
31
|
* Statically declare dependencies.
|
|
34
32
|
*/
|
|
@@ -38,59 +36,40 @@ class TakeProfitPerAsset {
|
|
|
38
36
|
|
|
39
37
|
/**
|
|
40
38
|
* Defines the output schema for this calculation.
|
|
41
|
-
* @returns {object} JSON Schema object
|
|
42
39
|
*/
|
|
43
40
|
static getSchema() {
|
|
44
41
|
const tickerSchema = {
|
|
45
42
|
"type": "object",
|
|
46
43
|
"properties": {
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
"avg_tp_pct_dist": {
|
|
52
|
-
"type": "number",
|
|
53
|
-
"description": "Average TP distance from open price % (for positions with TP)."
|
|
54
|
-
},
|
|
55
|
-
"tp_set_count": { "type": "number" },
|
|
56
|
-
"total_count": { "type": "number" },
|
|
57
|
-
"tp_set_rate_pct": {
|
|
58
|
-
"type": "number",
|
|
59
|
-
"description": "Percentage of positions that have a TP set."
|
|
60
|
-
}
|
|
44
|
+
"tp_usage_pct": { "type": "number" },
|
|
45
|
+
"tp_count": { "type": "number" },
|
|
46
|
+
"total_count": { "type": "number" }
|
|
61
47
|
},
|
|
62
|
-
"required": ["
|
|
48
|
+
"required": ["tp_usage_pct", "tp_count", "total_count"]
|
|
63
49
|
};
|
|
64
50
|
|
|
65
51
|
return {
|
|
66
52
|
"type": "object",
|
|
67
|
-
"description": "Calculates the
|
|
68
|
-
"patternProperties": {
|
|
69
|
-
"^.*$": tickerSchema // Ticker
|
|
70
|
-
},
|
|
53
|
+
"description": "Calculates the percentage of speculators using a take-profit per asset.",
|
|
54
|
+
"patternProperties": { "^.*$": tickerSchema },
|
|
71
55
|
"additionalProperties": tickerSchema
|
|
72
56
|
};
|
|
73
57
|
}
|
|
74
58
|
|
|
75
59
|
_initAsset(instrumentId) {
|
|
76
60
|
if (!this.assets.has(instrumentId)) {
|
|
77
|
-
this.assets.set(instrumentId, {
|
|
78
|
-
tp_rate_sum: 0,
|
|
79
|
-
tp_dist_sum: 0,
|
|
80
|
-
tp_set_count: 0,
|
|
81
|
-
total_count: 0
|
|
82
|
-
});
|
|
61
|
+
this.assets.set(instrumentId, { with_tp: 0, total: 0 });
|
|
83
62
|
}
|
|
84
63
|
}
|
|
85
64
|
|
|
86
|
-
// ---
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
65
|
+
// --- STANDARD 0: UPDATED SIGNATURE ---
|
|
66
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
67
|
+
// --- STANDARD 0: ADDED ---
|
|
68
|
+
if (!this.tickerMap) {
|
|
69
|
+
this.tickerMap = context.instrumentToTicker;
|
|
91
70
|
}
|
|
92
|
-
|
|
93
|
-
const positions =
|
|
71
|
+
|
|
72
|
+
const positions = todayPortfolio.PublicPositions;
|
|
94
73
|
if (!positions || !Array.isArray(positions)) {
|
|
95
74
|
return;
|
|
96
75
|
}
|
|
@@ -101,47 +80,44 @@ class TakeProfitPerAsset {
|
|
|
101
80
|
|
|
102
81
|
this._initAsset(instrumentId);
|
|
103
82
|
const assetData = this.assets.get(instrumentId);
|
|
104
|
-
assetData.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
assetData.
|
|
109
|
-
assetData.tp_rate_sum += tp_rate;
|
|
110
|
-
|
|
111
|
-
const open_rate = pos.OpenRate || 0;
|
|
112
|
-
if (open_rate > 0) {
|
|
113
|
-
const distance = Math.abs(tp_rate - open_rate);
|
|
114
|
-
assetData.tp_dist_sum += (distance / open_rate);
|
|
115
|
-
}
|
|
83
|
+
assetData.total++;
|
|
84
|
+
|
|
85
|
+
// PctToTakeProfit is -1 if no TP is set
|
|
86
|
+
if (pos.PctToTakeProfit >= 0) {
|
|
87
|
+
assetData.with_tp++;
|
|
116
88
|
}
|
|
117
89
|
}
|
|
118
90
|
}
|
|
119
91
|
|
|
120
92
|
async getResult() {
|
|
121
|
-
|
|
122
|
-
|
|
93
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
94
|
+
|
|
95
|
+
// Failsafe check
|
|
96
|
+
if (!this.tickerMap) {
|
|
97
|
+
return {}; // process() must run first
|
|
123
98
|
}
|
|
124
99
|
|
|
125
100
|
const result = {};
|
|
126
101
|
for (const [instrumentId, data] of this.assets.entries()) {
|
|
127
|
-
|
|
128
|
-
const
|
|
102
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
103
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
129
104
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
105
|
+
if (data.total > 0) {
|
|
106
|
+
result[ticker] = {
|
|
107
|
+
tp_usage_pct: (data.with_tp / data.total) * 100,
|
|
108
|
+
tp_count: data.with_tp,
|
|
109
|
+
total_count: data.total
|
|
110
|
+
};
|
|
111
|
+
}
|
|
137
112
|
}
|
|
138
113
|
return result;
|
|
139
114
|
}
|
|
140
115
|
|
|
141
116
|
reset() {
|
|
142
117
|
this.assets.clear();
|
|
143
|
-
|
|
118
|
+
// --- STANDARD 0: RENAMED ---
|
|
119
|
+
this.tickerMap = null;
|
|
144
120
|
}
|
|
145
121
|
}
|
|
146
122
|
|
|
147
|
-
module.exports =
|
|
123
|
+
module.exports = SpeculatorTakeProfitPerAsset;
|