aiden-shared-calculations-unified 1.0.86 → 1.0.87
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 +36 -106
- package/calculations/core/asset-position-size.js +40 -91
- package/calculations/core/average-daily-pnl-all-users.js +18 -57
- package/calculations/core/average-daily-pnl-per-sector.js +41 -88
- package/calculations/core/average-daily-pnl-per-stock.js +38 -91
- package/calculations/core/average-daily-position-pnl.js +19 -49
- package/calculations/core/holding-duration-per-asset.js +25 -127
- package/calculations/core/instrument-price-change-1d.js +30 -49
- package/calculations/core/instrument-price-momentum-20d.js +50 -60
- package/calculations/core/long-position-per-stock.js +39 -68
- package/calculations/core/overall-holding-duration.js +16 -87
- package/calculations/core/overall-profitability-ratio.js +11 -40
- package/calculations/core/platform-buy-sell-sentiment.js +41 -124
- package/calculations/core/platform-daily-bought-vs-sold-count.js +41 -99
- package/calculations/core/platform-daily-ownership-delta.js +68 -126
- package/calculations/core/platform-ownership-per-sector.js +45 -96
- package/calculations/core/platform-total-positions-held.js +20 -80
- package/calculations/core/pnl-distribution-per-stock.js +29 -135
- package/calculations/core/price-metrics.js +95 -206
- package/calculations/core/profitability-ratio-per-sector.js +34 -79
- package/calculations/core/profitability-ratio-per-stock.js +32 -88
- package/calculations/core/profitability-skew-per-stock.js +41 -94
- package/calculations/core/profitable-and-unprofitable-status.js +44 -76
- package/calculations/core/sentiment-per-stock.js +24 -77
- package/calculations/core/short-position-per-stock.js +35 -43
- package/calculations/core/social-activity-aggregation.js +26 -49
- package/calculations/core/social-asset-posts-trend.js +38 -94
- package/calculations/core/social-event-correlation.js +26 -93
- package/calculations/core/social-sentiment-aggregation.js +20 -44
- package/calculations/core/social-top-mentioned-words.js +35 -87
- package/calculations/core/social-topic-interest-evolution.js +22 -111
- package/calculations/core/social-topic-sentiment-matrix.js +38 -104
- package/calculations/core/social-word-mentions-trend.js +27 -104
- package/calculations/core/speculator-asset-sentiment.js +31 -72
- package/calculations/core/speculator-danger-zone.js +48 -84
- package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +20 -52
- package/calculations/core/speculator-distance-to-tp-per-leverage.js +23 -53
- package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +20 -50
- package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +23 -50
- package/calculations/core/speculator-leverage-per-asset.js +25 -64
- package/calculations/core/speculator-leverage-per-sector.js +27 -63
- package/calculations/core/speculator-risk-reward-ratio-per-asset.js +24 -53
- package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +55 -68
- package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +54 -71
- package/calculations/core/speculator-stop-loss-per-asset.js +19 -44
- package/calculations/core/speculator-take-profit-per-asset.js +20 -57
- package/calculations/core/speculator-tsl-per-asset.js +17 -56
- package/calculations/core/total-long-figures.js +16 -31
- package/calculations/core/total-long-per-sector.js +39 -61
- package/calculations/core/total-short-figures.js +13 -32
- package/calculations/core/total-short-per-sector.js +39 -61
- package/calculations/core/users-processed.js +11 -46
- package/calculations/gauss/cohort-capital-flow.js +54 -173
- package/calculations/gauss/cohort-definer.js +77 -163
- package/calculations/gauss/daily-dna-filter.js +29 -83
- package/calculations/gauss/gauss-divergence-signal.js +22 -109
- package/calculations/gem/cohort-momentum-state.js +27 -72
- package/calculations/gem/cohort-skill-definition.js +36 -52
- package/calculations/gem/platform-conviction-divergence.js +18 -60
- package/calculations/gem/quant-skill-alpha-signal.js +25 -98
- package/calculations/gem/skilled-cohort-flow.js +67 -175
- package/calculations/gem/skilled-unskilled-divergence.js +18 -73
- package/calculations/gem/unskilled-cohort-flow.js +64 -172
- package/calculations/helix/helix-contrarian-signal.js +20 -114
- package/calculations/helix/herd-consensus-score.js +42 -124
- package/calculations/helix/winner-loser-flow.js +36 -118
- package/calculations/pyro/risk-appetite-index.js +33 -74
- package/calculations/pyro/squeeze-potential.js +30 -87
- package/calculations/pyro/volatility-signal.js +33 -78
- package/package.json +1 -1
|
@@ -1,170 +1,100 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Core Metric (Pass 2)
|
|
3
|
-
*
|
|
4
|
-
* This 'standard' calculation streams all user portfolios
|
|
5
|
-
* and, for each asset, buckets users into "Winner" (in-profit)
|
|
6
|
-
* and "Loser" (in-loss) cohorts.
|
|
7
|
-
*
|
|
8
|
-
* It is a dependency for calculations that need to know
|
|
9
|
-
* the full list of users in each cohort (e.g., 'helix-contrarian-signal')
|
|
10
|
-
* but is too large to be a dependency for 'winner-loser-flow'.
|
|
3
|
+
* REFACTORED: Buckets users by P&L status (Win/Loss).
|
|
11
4
|
*/
|
|
12
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
13
|
-
|
|
14
5
|
class AssetPnlStatus {
|
|
15
6
|
constructor() {
|
|
16
|
-
// { [ticker]: { winners: [uid1, uid2], losers: [uid1] } }
|
|
17
7
|
this.tickerBuckets = new Map();
|
|
18
|
-
// { [sector]: { winners: [uid1, uid2], losers: [uid1] } }
|
|
19
8
|
this.sectorBuckets = new Map();
|
|
20
|
-
|
|
21
|
-
// --- STANDARD 0: RENAMED ---
|
|
22
|
-
this.tickerMap = null;
|
|
23
|
-
this.sectorMap = null;
|
|
24
|
-
|
|
25
9
|
}
|
|
26
10
|
|
|
27
|
-
/** Statically defines metadata */
|
|
28
11
|
static getMetadata() {
|
|
29
12
|
return {
|
|
30
13
|
type: 'standard',
|
|
31
14
|
rootDataDependencies: ['portfolio'],
|
|
32
|
-
isHistorical: false,
|
|
15
|
+
isHistorical: false,
|
|
33
16
|
userType: 'all',
|
|
34
17
|
category: 'core'
|
|
35
18
|
};
|
|
36
19
|
}
|
|
37
20
|
|
|
38
|
-
|
|
39
|
-
static getDependencies() {
|
|
40
|
-
return []; // This is a Pass 2 calculation
|
|
41
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
42
22
|
|
|
43
|
-
/**
|
|
44
|
-
* Defines the output schema for this calculation.
|
|
45
|
-
*/
|
|
46
23
|
static getSchema() {
|
|
47
24
|
const cohortSchema = {
|
|
48
25
|
"type": "object",
|
|
49
26
|
"properties": {
|
|
50
|
-
"winners": {
|
|
51
|
-
|
|
52
|
-
"items": { "type": "string" },
|
|
53
|
-
"description": "List of User IDs in the 'Winner' (in-profit) cohort."
|
|
54
|
-
},
|
|
55
|
-
"losers": {
|
|
56
|
-
"type": "array",
|
|
57
|
-
"items": { "type": "string" },
|
|
58
|
-
"description": "List of User IDs in the 'Loser' (in-loss) cohort."
|
|
59
|
-
}
|
|
27
|
+
"winners": { "type": "array", "items": { "type": "string" } },
|
|
28
|
+
"losers": { "type": "array", "items": { "type": "string" } }
|
|
60
29
|
},
|
|
61
30
|
"required": ["winners", "losers"]
|
|
62
31
|
};
|
|
63
|
-
|
|
64
32
|
return {
|
|
65
33
|
"type": "object",
|
|
66
|
-
"description": "Buckets all users into 'Winner' or 'Loser' cohorts per asset and sector.",
|
|
67
34
|
"properties": {
|
|
68
|
-
"by_ticker": {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"additionalProperties": cohortSchema
|
|
72
|
-
},
|
|
73
|
-
"by_sector": {
|
|
74
|
-
"type": "object",
|
|
75
|
-
"patternProperties": { "^.*$": cohortSchema },
|
|
76
|
-
"additionalProperties": cohortSchema
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
"required": ["by_ticker", "by_sector"]
|
|
35
|
+
"by_ticker": { "type": "object", "patternProperties": { "^.*$": cohortSchema } },
|
|
36
|
+
"by_sector": { "type": "object", "patternProperties": { "^.*$": cohortSchema } }
|
|
37
|
+
}
|
|
80
38
|
};
|
|
81
39
|
}
|
|
82
40
|
|
|
83
41
|
_init(map, key) {
|
|
84
|
-
if (!map.has(key)) {
|
|
85
|
-
map.set(key, { winners: new Set(), losers: new Set() });
|
|
86
|
-
}
|
|
42
|
+
if (!map.has(key)) map.set(key, { winners: new Set(), losers: new Set() });
|
|
87
43
|
}
|
|
88
44
|
|
|
89
|
-
process(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!this.tickerMap || !this.sectorMap) {
|
|
97
|
-
return; // Failsafe if context is missing maps
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const positions = todayPortfolio?.AggregatedPositions || todayPortfolio?.PublicPositions;
|
|
101
|
-
if (!positions || positions.length === 0) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
45
|
+
process(context) {
|
|
46
|
+
const { extract } = context.math;
|
|
47
|
+
const { mappings, user } = context;
|
|
48
|
+
|
|
49
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
104
50
|
|
|
105
51
|
for (const pos of positions) {
|
|
106
|
-
|
|
52
|
+
const instId = extract.getInstrumentId(pos);
|
|
53
|
+
if (!instId) continue;
|
|
107
54
|
|
|
108
|
-
const pnl = pos
|
|
109
|
-
if (pnl === 0) continue;
|
|
55
|
+
const pnl = extract.getNetProfit(pos);
|
|
56
|
+
if (pnl === 0) continue;
|
|
110
57
|
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
const sector = this.sectorMap[pos.InstrumentID];
|
|
58
|
+
const ticker = mappings.instrumentToTicker[instId];
|
|
59
|
+
const sector = mappings.sectorMapping[instId];
|
|
114
60
|
const isWinner = pnl > 0;
|
|
115
61
|
|
|
116
62
|
if (ticker) {
|
|
117
63
|
this._init(this.tickerBuckets, ticker);
|
|
118
64
|
const asset = this.tickerBuckets.get(ticker);
|
|
119
|
-
if (isWinner) asset.winners.add(
|
|
120
|
-
else asset.losers.add(
|
|
65
|
+
if (isWinner) asset.winners.add(user.id);
|
|
66
|
+
else asset.losers.add(user.id);
|
|
121
67
|
}
|
|
122
68
|
|
|
123
69
|
if (sector) {
|
|
124
70
|
this._init(this.sectorBuckets, sector);
|
|
125
71
|
const sec = this.sectorBuckets.get(sector);
|
|
126
|
-
if (isWinner) sec.winners.add(
|
|
127
|
-
else sec.losers.add(
|
|
72
|
+
if (isWinner) sec.winners.add(user.id);
|
|
73
|
+
else sec.losers.add(user.id);
|
|
128
74
|
}
|
|
129
75
|
}
|
|
130
76
|
}
|
|
131
77
|
|
|
132
78
|
async getResult() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// Failsafe check
|
|
136
|
-
if (!this.tickerMap) {
|
|
137
|
-
return { by_ticker: {}, by_sector: {} };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const result = {
|
|
141
|
-
by_ticker: {},
|
|
142
|
-
by_sector: {}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
for (const [ticker, data] of this.tickerBuckets.entries()) {
|
|
146
|
-
result.by_ticker[ticker] = {
|
|
147
|
-
winners: Array.from(data.winners),
|
|
148
|
-
losers: Array.from(data.losers)
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
for (const [sector, data] of this.sectorBuckets.entries()) {
|
|
153
|
-
result.by_sector[sector] = {
|
|
154
|
-
winners: Array.from(data.winners),
|
|
155
|
-
losers: Array.from(data.losers)
|
|
156
|
-
};
|
|
157
|
-
}
|
|
79
|
+
const result = { by_ticker: {}, by_sector: {} };
|
|
158
80
|
|
|
81
|
+
const buildResult = (map, out) => {
|
|
82
|
+
for (const [key, data] of map.entries()) {
|
|
83
|
+
out[key] = {
|
|
84
|
+
winners: Array.from(data.winners),
|
|
85
|
+
losers: Array.from(data.losers)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
buildResult(this.tickerBuckets, result.by_ticker);
|
|
91
|
+
buildResult(this.sectorBuckets, result.by_sector);
|
|
159
92
|
return result;
|
|
160
93
|
}
|
|
161
94
|
|
|
162
95
|
reset() {
|
|
163
96
|
this.tickerBuckets.clear();
|
|
164
97
|
this.sectorBuckets.clear();
|
|
165
|
-
// --- STANDARD 0: RENAMED ---
|
|
166
|
-
this.tickerMap = null;
|
|
167
|
-
this.sectorMap = null;
|
|
168
98
|
}
|
|
169
99
|
}
|
|
170
100
|
module.exports = AssetPnlStatus;
|
|
@@ -1,145 +1,97 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Core Metric (Pass 2)
|
|
3
|
-
*
|
|
4
|
-
* This 'standard' calculation streams all user portfolios
|
|
5
|
-
* and calculates the *average position size in $USD* for
|
|
6
|
-
* all holders of each asset, bucketed by ticker and sector.
|
|
3
|
+
* REFACTORED: Calculates average portfolio weight (%) per asset.
|
|
7
4
|
*/
|
|
8
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
9
|
-
|
|
10
5
|
class AssetPositionSize {
|
|
11
6
|
constructor() {
|
|
12
|
-
// { [ticker]: { invested_sum: 0, user_count: 0 } }
|
|
13
7
|
this.tickerBuckets = new Map();
|
|
14
|
-
// { [sector]: { invested_sum: 0, user_count: 0 } }
|
|
15
8
|
this.sectorBuckets = new Map();
|
|
16
|
-
|
|
17
|
-
// --- STANDARD 0: RENAMED ---
|
|
18
|
-
this.tickerMap = null;
|
|
19
|
-
this.sectorMap = null;
|
|
20
|
-
|
|
21
9
|
}
|
|
22
10
|
|
|
23
|
-
/** Statically defines metadata */
|
|
24
11
|
static getMetadata() {
|
|
25
12
|
return {
|
|
26
13
|
type: 'standard',
|
|
27
14
|
rootDataDependencies: ['portfolio'],
|
|
28
|
-
isHistorical: false,
|
|
15
|
+
isHistorical: false,
|
|
29
16
|
userType: 'all',
|
|
30
17
|
category: 'core'
|
|
31
18
|
};
|
|
32
19
|
}
|
|
33
20
|
|
|
34
|
-
|
|
35
|
-
static getDependencies() {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
38
22
|
|
|
39
|
-
/**
|
|
40
|
-
* Defines the output schema for this calculation.
|
|
41
|
-
*/
|
|
42
23
|
static getSchema() {
|
|
43
24
|
const schema = {
|
|
44
25
|
"type": "object",
|
|
45
26
|
"properties": {
|
|
46
|
-
"
|
|
47
|
-
"
|
|
27
|
+
"avg_position_weight": { "type": "number", "description": "Average % of portfolio allocated to this asset." },
|
|
28
|
+
"total_exposure_weight": { "type": "number", "description": "Sum of all user allocation percentages." },
|
|
48
29
|
"user_count": { "type": "number" }
|
|
49
30
|
},
|
|
50
|
-
"required": ["
|
|
31
|
+
"required": ["avg_position_weight", "total_exposure_weight", "user_count"]
|
|
51
32
|
};
|
|
52
|
-
|
|
53
33
|
return {
|
|
54
34
|
"type": "object",
|
|
55
|
-
"description": "Calculates the avg. $USD position size for all holders per asset/sector.",
|
|
56
35
|
"properties": {
|
|
57
|
-
"by_ticker": {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"additionalProperties": schema
|
|
61
|
-
},
|
|
62
|
-
"by_sector": {
|
|
63
|
-
"type": "object",
|
|
64
|
-
"patternProperties": { "^.*$": schema },
|
|
65
|
-
"additionalProperties": schema
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
"required": ["by_ticker", "by_sector"]
|
|
36
|
+
"by_ticker": { "type": "object", "patternProperties": { "^.*$": schema } },
|
|
37
|
+
"by_sector": { "type": "object", "patternProperties": { "^.*$": schema } }
|
|
38
|
+
}
|
|
69
39
|
};
|
|
70
40
|
}
|
|
71
41
|
|
|
72
42
|
_init(map, key) {
|
|
73
|
-
if (!map.has(key)) {
|
|
74
|
-
map.set(key, { invested_sum: 0, user_count: 0 });
|
|
75
|
-
}
|
|
43
|
+
if (!map.has(key)) map.set(key, { weight_sum: 0, user_count: 0 });
|
|
76
44
|
}
|
|
77
45
|
|
|
78
|
-
process(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!this.tickerMap || !this.sectorMap) {
|
|
86
|
-
return; // Failsafe if context is missing maps
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const positions = todayPortfolio?.AggregatedPositions || todayPortfolio?.PublicPositions;
|
|
90
|
-
if (!positions || positions.length === 0) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
46
|
+
process(context) {
|
|
47
|
+
const { extract } = context.math;
|
|
48
|
+
const { mappings, user } = context;
|
|
49
|
+
|
|
50
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
93
51
|
|
|
94
52
|
for (const pos of positions) {
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
53
|
+
const weight = extract.getPositionWeight(pos, user.type);
|
|
54
|
+
if (weight <= 0) continue;
|
|
55
|
+
|
|
56
|
+
const instId = extract.getInstrumentId(pos);
|
|
57
|
+
if (!instId) continue;
|
|
97
58
|
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
const sector = this.sectorMap[pos.InstrumentID];
|
|
59
|
+
const ticker = mappings.instrumentToTicker[instId];
|
|
60
|
+
const sector = mappings.sectorMapping[instId];
|
|
101
61
|
|
|
102
62
|
if (ticker) {
|
|
103
63
|
this._init(this.tickerBuckets, ticker);
|
|
104
64
|
const asset = this.tickerBuckets.get(ticker);
|
|
105
|
-
asset.
|
|
106
|
-
asset.user_count++;
|
|
65
|
+
asset.weight_sum += weight;
|
|
66
|
+
asset.user_count++;
|
|
107
67
|
}
|
|
108
68
|
|
|
109
69
|
if (sector) {
|
|
110
70
|
this._init(this.sectorBuckets, sector);
|
|
111
71
|
const sec = this.sectorBuckets.get(sector);
|
|
112
|
-
sec.
|
|
113
|
-
sec.user_count++;
|
|
72
|
+
sec.weight_sum += weight;
|
|
73
|
+
sec.user_count++;
|
|
114
74
|
}
|
|
115
75
|
}
|
|
116
76
|
}
|
|
117
77
|
|
|
118
78
|
async getResult() {
|
|
119
|
-
const result = {
|
|
120
|
-
by_ticker: {},
|
|
121
|
-
by_sector: {}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
for (const [ticker, data] of this.tickerBuckets.entries()) {
|
|
125
|
-
if (data.user_count > 0) {
|
|
126
|
-
result.by_ticker[ticker] = {
|
|
127
|
-
avg_position_usd: data.invested_sum / data.user_count,
|
|
128
|
-
total_invested_usd: data.invested_sum,
|
|
129
|
-
user_count: data.user_count
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
}
|
|
79
|
+
const result = { by_ticker: {}, by_sector: {} };
|
|
133
80
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
81
|
+
const buildResult = (map, out) => {
|
|
82
|
+
for (const [key, data] of map.entries()) {
|
|
83
|
+
if (data.user_count > 0) {
|
|
84
|
+
out[key] = {
|
|
85
|
+
avg_position_weight: data.weight_sum / data.user_count,
|
|
86
|
+
total_exposure_weight: data.weight_sum,
|
|
87
|
+
user_count: data.user_count
|
|
88
|
+
};
|
|
89
|
+
}
|
|
141
90
|
}
|
|
142
|
-
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
buildResult(this.tickerBuckets, result.by_ticker);
|
|
94
|
+
buildResult(this.sectorBuckets, result.by_sector);
|
|
143
95
|
|
|
144
96
|
return result;
|
|
145
97
|
}
|
|
@@ -147,9 +99,6 @@ class AssetPositionSize {
|
|
|
147
99
|
reset() {
|
|
148
100
|
this.tickerBuckets.clear();
|
|
149
101
|
this.sectorBuckets.clear();
|
|
150
|
-
// --- STANDARD 0: RENAMED ---
|
|
151
|
-
this.tickerMap = null;
|
|
152
|
-
this.sectorMap = null;
|
|
153
102
|
}
|
|
154
103
|
}
|
|
155
104
|
module.exports = AssetPositionSize;
|
|
@@ -1,97 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for average daily P&L
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "What was the average daily P&L
|
|
5
|
-
* for the average user?"
|
|
6
|
-
*
|
|
7
|
-
* This is a global health metric for the crowd.
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for average daily P&L %.
|
|
3
|
+
* REFACTORED: Uses standardized P&L % access.
|
|
8
4
|
*/
|
|
9
5
|
class AverageDailyPnlAllUsers {
|
|
10
6
|
constructor() {
|
|
11
|
-
this.
|
|
7
|
+
this.totalPnlPct = 0;
|
|
12
8
|
this.userCount = 0;
|
|
13
9
|
}
|
|
14
10
|
|
|
15
|
-
// --- NEW ---
|
|
16
|
-
/**
|
|
17
|
-
* Statically defines all metadata for the manifest builder.
|
|
18
|
-
*/
|
|
19
11
|
static getMetadata() {
|
|
20
12
|
return {
|
|
21
13
|
type: 'standard',
|
|
22
|
-
rootDataDependencies: ['portfolio'],
|
|
14
|
+
rootDataDependencies: ['portfolio'],
|
|
23
15
|
isHistorical: false,
|
|
24
16
|
userType: 'all',
|
|
25
17
|
category: 'core_pnl'
|
|
26
18
|
};
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Statically declare dependencies.
|
|
32
|
-
*/
|
|
33
|
-
static getDependencies() {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
36
22
|
|
|
37
|
-
/**
|
|
38
|
-
* Defines the output schema for this calculation.
|
|
39
|
-
* @returns {object} JSON Schema object
|
|
40
|
-
*/
|
|
41
23
|
static getSchema() {
|
|
42
24
|
return {
|
|
43
25
|
"type": "object",
|
|
44
|
-
"description": "Calculates the average daily P&L across all users.",
|
|
45
26
|
"properties": {
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
"total_pnl": {
|
|
51
|
-
"type": "number",
|
|
52
|
-
"description": "The sum of all daily P&L from all users."
|
|
53
|
-
},
|
|
54
|
-
"user_count": {
|
|
55
|
-
"type": "number",
|
|
56
|
-
"description": "The total number of users processed."
|
|
57
|
-
}
|
|
27
|
+
"average_daily_pnl_pct": { "type": "number" },
|
|
28
|
+
"total_aggregate_pnl_pct": { "type": "number", "description": "Sum of all user daily P&L percentages." },
|
|
29
|
+
"user_count": { "type": "number" }
|
|
58
30
|
},
|
|
59
|
-
"required": ["
|
|
31
|
+
"required": ["average_daily_pnl_pct", "total_aggregate_pnl_pct", "user_count"]
|
|
60
32
|
};
|
|
61
33
|
}
|
|
62
34
|
|
|
63
|
-
process(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Check if it's a SPECULATOR portfolio (it has a root 'NetProfit' and 'PublicPositions')
|
|
67
|
-
if (todayPortfolio.NetProfit !== undefined && todayPortfolio.PublicPositions) {
|
|
68
|
-
|
|
69
|
-
dailyPnl = todayPortfolio.NetProfit;
|
|
35
|
+
process(context) {
|
|
36
|
+
const { extract } = context.math;
|
|
37
|
+
const { user } = context;
|
|
70
38
|
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Sum the P&L by calculating (Value - Invested) from the aggregates
|
|
75
|
-
dailyPnl = todayPortfolio.AggregatedPositionsByInstrumentTypeID.reduce((sum, agg) => {
|
|
76
|
-
return sum + (agg.Value - agg.Invested);
|
|
77
|
-
}, 0);
|
|
78
|
-
|
|
79
|
-
}
|
|
39
|
+
// extract.getPortfolioDailyPnl returns the daily P&L as a percentage for the user
|
|
40
|
+
const dailyPnl = extract.getPortfolioDailyPnl(user.portfolio.today, user.type);
|
|
80
41
|
|
|
81
|
-
this.
|
|
42
|
+
this.totalPnlPct += dailyPnl;
|
|
82
43
|
this.userCount++;
|
|
83
44
|
}
|
|
84
45
|
|
|
85
46
|
getResult() {
|
|
86
47
|
return {
|
|
87
|
-
|
|
88
|
-
|
|
48
|
+
average_daily_pnl_pct: (this.userCount > 0) ? (this.totalPnlPct / this.userCount) : 0,
|
|
49
|
+
total_aggregate_pnl_pct: this.totalPnlPct,
|
|
89
50
|
user_count: this.userCount
|
|
90
51
|
};
|
|
91
52
|
}
|
|
92
53
|
|
|
93
54
|
reset() {
|
|
94
|
-
this.
|
|
55
|
+
this.totalPnlPct = 0;
|
|
95
56
|
this.userCount = 0;
|
|
96
57
|
}
|
|
97
58
|
}
|