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,152 +1,170 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Core Metric (Pass 2)
|
|
3
3
|
*
|
|
4
|
-
* This
|
|
5
|
-
*
|
|
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.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* This version is modified to *only* store counts. The
|
|
11
|
-
* `users_in_profit` and `users_in_loss` arrays are removed
|
|
12
|
-
* to prevent exceeding the 1 MiB Firestore document size limit.
|
|
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'.
|
|
13
11
|
*/
|
|
14
|
-
|
|
12
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
15
13
|
|
|
16
14
|
class AssetPnlStatus {
|
|
17
15
|
constructor() {
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.
|
|
22
|
-
|
|
16
|
+
// { [ticker]: { winners: [uid1, uid2], losers: [uid1] } }
|
|
17
|
+
this.tickerBuckets = new Map();
|
|
18
|
+
// { [sector]: { winners: [uid1, uid2], losers: [uid1] } }
|
|
19
|
+
this.sectorBuckets = new Map();
|
|
20
|
+
|
|
21
|
+
// --- STANDARD 0: RENAMED ---
|
|
22
|
+
this.tickerMap = null;
|
|
23
|
+
this.sectorMap = null;
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Statically defines metadata */
|
|
28
|
+
static getMetadata() {
|
|
29
|
+
return {
|
|
30
|
+
type: 'standard',
|
|
31
|
+
rootDataDependencies: ['portfolio'],
|
|
32
|
+
isHistorical: false, // Reads today's portfolio
|
|
33
|
+
userType: 'all',
|
|
34
|
+
category: 'core'
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Statically declare dependencies */
|
|
39
|
+
static getDependencies() {
|
|
40
|
+
return []; // This is a Pass 2 calculation
|
|
23
41
|
}
|
|
24
42
|
|
|
25
43
|
/**
|
|
26
44
|
* Defines the output schema for this calculation.
|
|
27
|
-
* --- MODIFIED ---
|
|
28
|
-
* Removed the `users_in_profit` and `users_in_loss` arrays.
|
|
29
|
-
* @returns {object} JSON Schema object
|
|
30
45
|
*/
|
|
31
46
|
static getSchema() {
|
|
32
|
-
const
|
|
47
|
+
const cohortSchema = {
|
|
33
48
|
"type": "object",
|
|
34
|
-
"description": "P&L status for a specific asset.",
|
|
35
49
|
"properties": {
|
|
36
|
-
"
|
|
37
|
-
"type": "
|
|
38
|
-
"
|
|
50
|
+
"winners": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": { "type": "string" },
|
|
53
|
+
"description": "List of User IDs in the 'Winner' (in-profit) cohort."
|
|
39
54
|
},
|
|
40
|
-
"
|
|
41
|
-
"type": "
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
"profit_ratio": {
|
|
45
|
-
"type": "number",
|
|
46
|
-
"description": "Percentage of users in profit (In Profit / Total)."
|
|
55
|
+
"losers": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": { "type": "string" },
|
|
58
|
+
"description": "List of User IDs in the 'Loser' (in-loss) cohort."
|
|
47
59
|
}
|
|
48
60
|
},
|
|
49
|
-
"required": ["
|
|
61
|
+
"required": ["winners", "losers"]
|
|
50
62
|
};
|
|
51
|
-
|
|
63
|
+
|
|
52
64
|
return {
|
|
53
65
|
"type": "object",
|
|
54
|
-
"description": "
|
|
55
|
-
"
|
|
56
|
-
"
|
|
66
|
+
"description": "Buckets all users into 'Winner' or 'Loser' cohorts per asset and sector.",
|
|
67
|
+
"properties": {
|
|
68
|
+
"by_ticker": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"patternProperties": { "^.*$": cohortSchema },
|
|
71
|
+
"additionalProperties": cohortSchema
|
|
72
|
+
},
|
|
73
|
+
"by_sector": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"patternProperties": { "^.*$": cohortSchema },
|
|
76
|
+
"additionalProperties": cohortSchema
|
|
77
|
+
}
|
|
57
78
|
},
|
|
58
|
-
"
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Statically defines all metadata for the manifest builder.
|
|
64
|
-
*/
|
|
65
|
-
static getMetadata() {
|
|
66
|
-
return {
|
|
67
|
-
type: 'standard',
|
|
68
|
-
rootDataDependencies: ['portfolio'],
|
|
69
|
-
isHistorical: false,
|
|
70
|
-
userType: 'all',
|
|
71
|
-
category: 'core_pnl'
|
|
79
|
+
"required": ["by_ticker", "by_sector"]
|
|
72
80
|
};
|
|
73
81
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
static getDependencies() {
|
|
79
|
-
return [];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
_initAsset(instrumentId) {
|
|
83
|
-
if (!this.assets.has(instrumentId)) {
|
|
84
|
-
this.assets.set(instrumentId, {
|
|
85
|
-
in_profit: new Set(),
|
|
86
|
-
in_loss: new Set()
|
|
87
|
-
});
|
|
82
|
+
|
|
83
|
+
_init(map, key) {
|
|
84
|
+
if (!map.has(key)) {
|
|
85
|
+
map.set(key, { winners: new Set(), losers: new Set() });
|
|
88
86
|
}
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
process(
|
|
92
|
-
|
|
93
|
-
if (!
|
|
89
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
90
|
+
// --- STANDARD 0: FIXED ---
|
|
91
|
+
if (!this.tickerMap) {
|
|
92
|
+
this.tickerMap = context.instrumentToTicker;
|
|
93
|
+
this.sectorMap = context.sectorMapping;
|
|
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) {
|
|
94
102
|
return;
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
for (const pos of positions) {
|
|
98
|
-
|
|
99
|
-
if (!instrumentId) continue;
|
|
106
|
+
if (!pos.InstrumentID) continue;
|
|
100
107
|
|
|
101
|
-
this._initAsset(instrumentId);
|
|
102
|
-
const asset = this.assets.get(instrumentId);
|
|
103
108
|
const pnl = pos.NetProfit || 0;
|
|
109
|
+
if (pnl === 0) continue; // Ignore neutral
|
|
110
|
+
|
|
111
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
112
|
+
const ticker = this.tickerMap[pos.InstrumentID];
|
|
113
|
+
const sector = this.sectorMap[pos.InstrumentID];
|
|
114
|
+
const isWinner = pnl > 0;
|
|
104
115
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
asset.
|
|
108
|
-
asset.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
if (ticker) {
|
|
117
|
+
this._init(this.tickerBuckets, ticker);
|
|
118
|
+
const asset = this.tickerBuckets.get(ticker);
|
|
119
|
+
if (isWinner) asset.winners.add(userId);
|
|
120
|
+
else asset.losers.add(userId);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (sector) {
|
|
124
|
+
this._init(this.sectorBuckets, sector);
|
|
125
|
+
const sec = this.sectorBuckets.get(sector);
|
|
126
|
+
if (isWinner) sec.winners.add(userId);
|
|
127
|
+
else sec.losers.add(userId);
|
|
112
128
|
}
|
|
113
129
|
}
|
|
114
130
|
}
|
|
115
131
|
|
|
116
|
-
/**
|
|
117
|
-
* --- MODIFIED ---
|
|
118
|
-
* This now saves only counts.
|
|
119
|
-
*/
|
|
120
132
|
async getResult() {
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
134
|
+
|
|
135
|
+
// Failsafe check
|
|
136
|
+
if (!this.tickerMap) {
|
|
137
|
+
return { by_ticker: {}, by_sector: {} };
|
|
123
138
|
}
|
|
124
139
|
|
|
125
|
-
const result = {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
in_profit_count: profitCount,
|
|
136
|
-
in_loss_count: lossCount,
|
|
137
|
-
profit_ratio: (profitCount / total) * 100
|
|
138
|
-
// Removed the user arrays
|
|
139
|
-
};
|
|
140
|
-
}
|
|
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
|
+
};
|
|
141
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
|
+
}
|
|
158
|
+
|
|
142
159
|
return result;
|
|
143
160
|
}
|
|
144
|
-
|
|
161
|
+
|
|
145
162
|
reset() {
|
|
146
|
-
this.
|
|
147
|
-
this.
|
|
148
|
-
|
|
163
|
+
this.tickerBuckets.clear();
|
|
164
|
+
this.sectorBuckets.clear();
|
|
165
|
+
// --- STANDARD 0: RENAMED ---
|
|
166
|
+
this.tickerMap = null;
|
|
167
|
+
this.sectorMap = null;
|
|
149
168
|
}
|
|
150
169
|
}
|
|
151
|
-
|
|
152
170
|
module.exports = AssetPnlStatus;
|
|
@@ -1,118 +1,155 @@
|
|
|
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
7
|
*/
|
|
4
|
-
|
|
8
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
5
9
|
|
|
6
10
|
class AssetPositionSize {
|
|
7
11
|
constructor() {
|
|
8
|
-
|
|
9
|
-
this.
|
|
12
|
+
// { [ticker]: { invested_sum: 0, user_count: 0 } }
|
|
13
|
+
this.tickerBuckets = new Map();
|
|
14
|
+
// { [sector]: { invested_sum: 0, user_count: 0 } }
|
|
15
|
+
this.sectorBuckets = new Map();
|
|
16
|
+
|
|
17
|
+
// --- STANDARD 0: RENAMED ---
|
|
18
|
+
this.tickerMap = null;
|
|
19
|
+
this.sectorMap = null;
|
|
20
|
+
|
|
10
21
|
}
|
|
11
22
|
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Statically defines all metadata for the manifest builder.
|
|
15
|
-
*/
|
|
23
|
+
/** Statically defines metadata */
|
|
16
24
|
static getMetadata() {
|
|
17
25
|
return {
|
|
18
|
-
type: 'standard',
|
|
19
|
-
rootDataDependencies: ['portfolio'],
|
|
20
|
-
isHistorical: false, //
|
|
21
|
-
userType: 'all',
|
|
22
|
-
category: '
|
|
26
|
+
type: 'standard',
|
|
27
|
+
rootDataDependencies: ['portfolio'],
|
|
28
|
+
isHistorical: false, // Reads today's portfolio
|
|
29
|
+
userType: 'all',
|
|
30
|
+
category: 'core'
|
|
23
31
|
};
|
|
24
32
|
}
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Statically declare dependencies.
|
|
29
|
-
*/
|
|
34
|
+
/** Statically declare dependencies */
|
|
30
35
|
static getDependencies() {
|
|
31
|
-
return [];
|
|
36
|
+
return [];
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Defines the output schema for this calculation.
|
|
36
|
-
* @returns {object} JSON Schema object
|
|
37
41
|
*/
|
|
38
42
|
static getSchema() {
|
|
43
|
+
const schema = {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"avg_position_usd": { "type": "number" },
|
|
47
|
+
"total_invested_usd": { "type": "number" },
|
|
48
|
+
"user_count": { "type": "number" }
|
|
49
|
+
},
|
|
50
|
+
"required": ["avg_position_usd", "total_invested_usd", "user_count"]
|
|
51
|
+
};
|
|
52
|
+
|
|
39
53
|
return {
|
|
40
54
|
"type": "object",
|
|
41
|
-
"description": "Calculates the
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
"description": "Calculates the avg. $USD position size for all holders per asset/sector.",
|
|
56
|
+
"properties": {
|
|
57
|
+
"by_ticker": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"patternProperties": { "^.*$": schema },
|
|
60
|
+
"additionalProperties": schema
|
|
61
|
+
},
|
|
62
|
+
"by_sector": {
|
|
45
63
|
"type": "object",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"average_position_size": {
|
|
49
|
-
"type": "number",
|
|
50
|
-
"description": "The average portfolio percentage allocated to this asset by users who hold it."
|
|
51
|
-
},
|
|
52
|
-
"position_count": {
|
|
53
|
-
"type": "number",
|
|
54
|
-
"description": "The total number of positions held in this asset across the user sample."
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
"required": ["average_position_size", "position_count"]
|
|
64
|
+
"patternProperties": { "^.*$": schema },
|
|
65
|
+
"additionalProperties": schema
|
|
58
66
|
}
|
|
59
67
|
},
|
|
60
|
-
"
|
|
61
|
-
"type": "object",
|
|
62
|
-
"properties": {
|
|
63
|
-
"average_position_size": { "type": "number" },
|
|
64
|
-
"position_count": { "type": "number" }
|
|
65
|
-
}
|
|
66
|
-
}
|
|
68
|
+
"required": ["by_ticker", "by_sector"]
|
|
67
69
|
};
|
|
68
70
|
}
|
|
71
|
+
|
|
72
|
+
_init(map, key) {
|
|
73
|
+
if (!map.has(key)) {
|
|
74
|
+
map.set(key, { invested_sum: 0, user_count: 0 });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
69
77
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
79
|
+
// --- STANDARD 0: FIXED ---
|
|
80
|
+
if (!this.tickerMap) {
|
|
81
|
+
this.tickerMap = context.instrumentToTicker;
|
|
82
|
+
this.sectorMap = context.sectorMapping;
|
|
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
|
+
}
|
|
75
93
|
|
|
76
|
-
for (const
|
|
77
|
-
const
|
|
78
|
-
if (!
|
|
94
|
+
for (const pos of positions) {
|
|
95
|
+
const invested = pos.Invested || 0;
|
|
96
|
+
if (!pos.InstrumentID || invested <= 0) continue;
|
|
97
|
+
|
|
98
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
99
|
+
const ticker = this.tickerMap[pos.InstrumentID];
|
|
100
|
+
const sector = this.sectorMap[pos.InstrumentID];
|
|
79
101
|
|
|
80
|
-
if (
|
|
81
|
-
this.
|
|
102
|
+
if (ticker) {
|
|
103
|
+
this._init(this.tickerBuckets, ticker);
|
|
104
|
+
const asset = this.tickerBuckets.get(ticker);
|
|
105
|
+
asset.invested_sum += invested;
|
|
106
|
+
asset.user_count++; // Count each position
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (sector) {
|
|
110
|
+
this._init(this.sectorBuckets, sector);
|
|
111
|
+
const sec = this.sectorBuckets.get(sector);
|
|
112
|
+
sec.invested_sum += invested;
|
|
113
|
+
sec.user_count++; // Count each position
|
|
82
114
|
}
|
|
83
|
-
|
|
84
|
-
this.assets[instrumentId].position_count++;
|
|
85
|
-
// FIX: Use the 'Invested' field, which holds the portfolio percentage
|
|
86
|
-
this.assets[instrumentId].position_value_sum += (position.Invested || 0);
|
|
87
115
|
}
|
|
88
116
|
}
|
|
89
117
|
|
|
90
118
|
async getResult() {
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
}
|
|
93
132
|
}
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
result[ticker] = {
|
|
102
|
-
// This is now the average *percentage* size
|
|
103
|
-
average_position_size: data.position_value_sum / data.position_count,
|
|
104
|
-
position_count: data.position_count
|
|
133
|
+
|
|
134
|
+
for (const [sector, data] of this.sectorBuckets.entries()) {
|
|
135
|
+
if (data.user_count > 0) {
|
|
136
|
+
result.by_sector[sector] = {
|
|
137
|
+
avg_position_usd: data.invested_sum / data.user_count,
|
|
138
|
+
total_invested_usd: data.invested_sum,
|
|
139
|
+
user_count: data.user_count
|
|
105
140
|
};
|
|
106
141
|
}
|
|
107
142
|
}
|
|
108
143
|
|
|
109
144
|
return result;
|
|
110
145
|
}
|
|
111
|
-
|
|
146
|
+
|
|
112
147
|
reset() {
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
148
|
+
this.tickerBuckets.clear();
|
|
149
|
+
this.sectorBuckets.clear();
|
|
150
|
+
// --- STANDARD 0: RENAMED ---
|
|
151
|
+
this.tickerMap = null;
|
|
152
|
+
this.sectorMap = null;
|
|
115
153
|
}
|
|
116
154
|
}
|
|
117
|
-
|
|
118
155
|
module.exports = AssetPositionSize;
|
|
@@ -60,9 +60,23 @@ class AverageDailyPnlAllUsers {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
process(
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
64
|
+
let dailyPnl = 0;
|
|
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;
|
|
70
|
+
|
|
71
|
+
// Check if it's a NORMAL user portfolio (it has 'AggregatedPositionsByInstrumentTypeID')
|
|
72
|
+
} else if (todayPortfolio.AggregatedPositionsByInstrumentTypeID) {
|
|
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
|
+
}
|
|
66
80
|
|
|
67
81
|
this.totalPnl += dailyPnl;
|
|
68
82
|
this.userCount++;
|