aiden-shared-calculations-unified 1.0.86 → 1.0.88
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/capitulation/asset-volatility-estimator.js +96 -0
- package/calculations/capitulation/retail-capitulation-risk-forecast.js +173 -0
- package/calculations/core/asset-cost-basis-profile.js +127 -0
- 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/test..js +0 -0
- 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/ghost-book/cost-basis-density.js +79 -0
- package/calculations/ghost-book/liquidity-vacuum.js +52 -0
- package/calculations/ghost-book/retail-gamma-exposure.js +86 -0
- 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/predicative-alpha/cognitive-dissonance.js +113 -0
- package/calculations/predicative-alpha/diamond-hand-fracture.js +90 -0
- package/calculations/predicative-alpha/mimetic-latency.js +124 -0
- 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,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
|
}
|
|
@@ -1,136 +1,89 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
* This 'standard' calculation streams all user portfolios
|
|
5
|
-
* and calculates the average daily P&L (in %) for all
|
|
6
|
-
* users, bucketed by sector.
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for average daily P&L % per sector.
|
|
3
|
+
* REFACTORED: Aggregates P&L percentages.
|
|
7
4
|
*/
|
|
8
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
9
|
-
|
|
10
5
|
class AverageDailyPnlPerSector {
|
|
11
6
|
constructor() {
|
|
12
|
-
|
|
13
|
-
this.sectorBuckets = new Map();
|
|
14
|
-
|
|
15
|
-
// --- STANDARD 0: RENAMED ---
|
|
7
|
+
this.sectorData = new Map();
|
|
16
8
|
this.sectorMap = null;
|
|
17
9
|
}
|
|
18
10
|
|
|
19
|
-
/** Statically defines metadata */
|
|
20
11
|
static getMetadata() {
|
|
21
12
|
return {
|
|
22
13
|
type: 'standard',
|
|
23
14
|
rootDataDependencies: ['portfolio'],
|
|
24
|
-
isHistorical: false,
|
|
15
|
+
isHistorical: false,
|
|
25
16
|
userType: 'all',
|
|
26
|
-
category: '
|
|
17
|
+
category: 'core_pnl'
|
|
27
18
|
};
|
|
28
19
|
}
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
static getDependencies() {
|
|
32
|
-
return [];
|
|
33
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
34
22
|
|
|
35
|
-
/**
|
|
36
|
-
* Defines the output schema for this calculation.
|
|
37
|
-
*/
|
|
38
23
|
static getSchema() {
|
|
39
|
-
const
|
|
24
|
+
const sectorSchema = {
|
|
40
25
|
"type": "object",
|
|
41
26
|
"properties": {
|
|
42
27
|
"avg_daily_pnl_pct": { "type": "number" },
|
|
43
|
-
"
|
|
28
|
+
"aggregate_pnl_pct_sum": { "type": "number", "description": "Sum of P&L % points for sector." },
|
|
44
29
|
"user_count": { "type": "number" }
|
|
45
30
|
},
|
|
46
|
-
"required": ["avg_daily_pnl_pct", "
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
"type": "object",
|
|
51
|
-
"description": "Calculates the avg. daily P&L % for all users, bucketed by sector.",
|
|
52
|
-
"patternProperties": { "^.*$": schema },
|
|
53
|
-
"additionalProperties": schema
|
|
31
|
+
"required": ["avg_daily_pnl_pct", "aggregate_pnl_pct_sum", "user_count"]
|
|
54
32
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
_init(map, key) {
|
|
58
|
-
if (!map.has(key)) {
|
|
59
|
-
map.set(key, { pnl_sum: 0, count: 0 });
|
|
60
|
-
}
|
|
33
|
+
return { "type": "object", "patternProperties": { "^.*$": sectorSchema } };
|
|
61
34
|
}
|
|
62
35
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.sectorMap = context.sectorMapping;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!this.sectorMap) {
|
|
70
|
-
return; // Failsafe if context is missing map
|
|
36
|
+
_initSector(sector) {
|
|
37
|
+
if (!this.sectorData.has(sector)) {
|
|
38
|
+
this.sectorData.set(sector, { total_pnl_pct: 0, count: 0 });
|
|
71
39
|
}
|
|
72
|
-
|
|
73
|
-
let dailyPnl = 0;
|
|
40
|
+
}
|
|
74
41
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
42
|
+
process(context) {
|
|
43
|
+
const { extract } = context.math;
|
|
44
|
+
const { mappings, user } = context;
|
|
45
|
+
if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
|
|
79
46
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Sum the P&L by calculating (Value - Invested) from the aggregates
|
|
84
|
-
dailyPnl = todayPortfolio.AggregatedPositionsByInstrumentTypeID.reduce((sum, agg) => {
|
|
85
|
-
return sum + (agg.Value - agg.Invested);
|
|
86
|
-
}, 0);
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
if (dailyPnl === 0) return; // No P&L
|
|
90
|
-
|
|
91
|
-
const positions = todayPortfolio?.AggregatedPositions || todayPortfolio?.PublicPositions;
|
|
92
|
-
if (!positions || positions.length === 0) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
47
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
48
|
+
const userSectorPnL = new Map();
|
|
95
49
|
|
|
96
|
-
// We only care about sectors this user *holds*
|
|
97
|
-
const heldSectors = new Set();
|
|
98
50
|
for (const pos of positions) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
51
|
+
const instId = extract.getInstrumentId(pos);
|
|
52
|
+
if (!instId) continue;
|
|
53
|
+
|
|
54
|
+
const sector = this.sectorMap[instId] || 'Unknown';
|
|
55
|
+
// NetProfit is the % profit relative to invested capital for that position
|
|
56
|
+
// We sum these to get the user's "sector performance points"
|
|
57
|
+
const pnlPct = extract.getNetProfit(pos);
|
|
58
|
+
|
|
59
|
+
if (!userSectorPnL.has(sector)) userSectorPnL.set(sector, 0);
|
|
60
|
+
userSectorPnL.set(sector, userSectorPnL.get(sector) + pnlPct);
|
|
105
61
|
}
|
|
106
|
-
|
|
107
|
-
for (const sector of
|
|
108
|
-
this.
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
62
|
+
|
|
63
|
+
for (const [sector, pnl] of userSectorPnL.entries()) {
|
|
64
|
+
this._initSector(sector);
|
|
65
|
+
const data = this.sectorData.get(sector);
|
|
66
|
+
data.total_pnl_pct += pnl;
|
|
67
|
+
data.count++;
|
|
112
68
|
}
|
|
113
69
|
}
|
|
114
70
|
|
|
115
71
|
async getResult() {
|
|
116
72
|
const result = {};
|
|
117
|
-
|
|
118
|
-
for (const [sector, data] of this.sectorBuckets.entries()) {
|
|
73
|
+
for (const [sector, data] of this.sectorData.entries()) {
|
|
119
74
|
if (data.count > 0) {
|
|
120
75
|
result[sector] = {
|
|
121
|
-
avg_daily_pnl_pct:
|
|
122
|
-
|
|
76
|
+
avg_daily_pnl_pct: data.total_pnl_pct / data.count,
|
|
77
|
+
aggregate_pnl_pct_sum: data.total_pnl_pct,
|
|
123
78
|
user_count: data.count
|
|
124
79
|
};
|
|
125
80
|
}
|
|
126
81
|
}
|
|
127
|
-
|
|
128
82
|
return result;
|
|
129
83
|
}
|
|
130
|
-
|
|
84
|
+
|
|
131
85
|
reset() {
|
|
132
|
-
this.
|
|
133
|
-
// --- STANDARD 0: RENAMED ---
|
|
86
|
+
this.sectorData.clear();
|
|
134
87
|
this.sectorMap = null;
|
|
135
88
|
}
|
|
136
89
|
}
|
|
@@ -1,136 +1,83 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
*
|
|
4
|
-
* This 'standard' calculation streams all user portfolios
|
|
5
|
-
* and calculates the average daily P&L (in %) for all
|
|
6
|
-
* users, bucketed by stock ticker.
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for average daily P&L % per stock.
|
|
3
|
+
* REFACTORED: Aggregates P&L percentages.
|
|
7
4
|
*/
|
|
8
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
9
|
-
|
|
10
5
|
class AverageDailyPnlPerStock {
|
|
11
6
|
constructor() {
|
|
12
|
-
|
|
13
|
-
this.tickerBuckets = new Map();
|
|
14
|
-
|
|
15
|
-
// --- STANDARD 0: RENAMED ---
|
|
7
|
+
this.stockData = new Map();
|
|
16
8
|
this.tickerMap = null;
|
|
17
9
|
}
|
|
18
10
|
|
|
19
|
-
/** Statically defines metadata */
|
|
20
11
|
static getMetadata() {
|
|
21
12
|
return {
|
|
22
13
|
type: 'standard',
|
|
23
14
|
rootDataDependencies: ['portfolio'],
|
|
24
|
-
isHistorical: false,
|
|
15
|
+
isHistorical: false,
|
|
25
16
|
userType: 'all',
|
|
26
|
-
category: '
|
|
17
|
+
category: 'core_pnl'
|
|
27
18
|
};
|
|
28
19
|
}
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
static getDependencies() {
|
|
32
|
-
return [];
|
|
33
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
34
22
|
|
|
35
|
-
/**
|
|
36
|
-
* Defines the output schema for this calculation.
|
|
37
|
-
*/
|
|
38
23
|
static getSchema() {
|
|
39
|
-
const
|
|
24
|
+
const stockSchema = {
|
|
40
25
|
"type": "object",
|
|
41
26
|
"properties": {
|
|
42
27
|
"avg_daily_pnl_pct": { "type": "number" },
|
|
43
|
-
"
|
|
44
|
-
"
|
|
28
|
+
"aggregate_pnl_pct_sum": { "type": "number" },
|
|
29
|
+
"position_count": { "type": "number" }
|
|
45
30
|
},
|
|
46
|
-
"required": ["avg_daily_pnl_pct", "
|
|
31
|
+
"required": ["avg_daily_pnl_pct", "aggregate_pnl_pct_sum", "position_count"]
|
|
47
32
|
};
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
"type": "object",
|
|
51
|
-
"description": "Calculates the avg. daily P&L % for all users, bucketed by stock.",
|
|
52
|
-
"patternProperties": { "^.*$": schema },
|
|
53
|
-
"additionalProperties": schema
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
_init(map, key) {
|
|
58
|
-
if (!map.has(key)) {
|
|
59
|
-
map.set(key, { pnl_sum: 0, count: 0 });
|
|
60
|
-
}
|
|
33
|
+
return { "type": "object", "patternProperties": { "^.*$": stockSchema } };
|
|
61
34
|
}
|
|
62
35
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.tickerMap = context.instrumentToTicker;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!this.tickerMap) {
|
|
70
|
-
return; // Failsafe if context is missing map
|
|
36
|
+
_initStock(instId) {
|
|
37
|
+
if (!this.stockData.has(instId)) {
|
|
38
|
+
this.stockData.set(instId, { total_pnl_pct: 0, count: 0 });
|
|
71
39
|
}
|
|
72
|
-
|
|
73
|
-
let dailyPnl = 0;
|
|
40
|
+
}
|
|
74
41
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
42
|
+
process(context) {
|
|
43
|
+
const { extract } = context.math;
|
|
44
|
+
const { mappings, user } = context;
|
|
45
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
79
46
|
|
|
80
|
-
|
|
81
|
-
} else if (todayPortfolio.AggregatedPositionsByInstrumentTypeID) {
|
|
82
|
-
|
|
83
|
-
// Sum the P&L by calculating (Value - Invested) from the aggregates
|
|
84
|
-
dailyPnl = todayPortfolio.AggregatedPositionsByInstrumentTypeID.reduce((sum, agg) => {
|
|
85
|
-
return sum + (agg.Value - agg.Invested);
|
|
86
|
-
}, 0);
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
if (dailyPnl === 0) return; // No P&L
|
|
90
|
-
|
|
91
|
-
const positions = todayPortfolio?.AggregatedPositions || todayPortfolio?.PublicPositions;
|
|
92
|
-
if (!positions || positions.length === 0) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
47
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
95
48
|
|
|
96
|
-
// We only care about tickers this user *holds*
|
|
97
|
-
const heldTickers = new Set();
|
|
98
49
|
for (const pos of positions) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const asset = this.tickerBuckets.get(ticker);
|
|
110
|
-
asset.pnl_sum += dailyPnl;
|
|
111
|
-
asset.count++;
|
|
50
|
+
const instId = extract.getInstrumentId(pos);
|
|
51
|
+
if (!instId) continue;
|
|
52
|
+
|
|
53
|
+
// NetProfit is %
|
|
54
|
+
const pnlPct = extract.getNetProfit(pos);
|
|
55
|
+
|
|
56
|
+
this._initStock(instId);
|
|
57
|
+
const data = this.stockData.get(instId);
|
|
58
|
+
data.total_pnl_pct += pnlPct;
|
|
59
|
+
data.count++;
|
|
112
60
|
}
|
|
113
61
|
}
|
|
114
62
|
|
|
115
63
|
async getResult() {
|
|
64
|
+
if (!this.tickerMap) return {};
|
|
116
65
|
const result = {};
|
|
117
|
-
|
|
118
|
-
|
|
66
|
+
for (const [instId, data] of this.stockData.entries()) {
|
|
67
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
119
68
|
if (data.count > 0) {
|
|
120
69
|
result[ticker] = {
|
|
121
|
-
avg_daily_pnl_pct:
|
|
122
|
-
|
|
123
|
-
|
|
70
|
+
avg_daily_pnl_pct: data.total_pnl_pct / data.count,
|
|
71
|
+
aggregate_pnl_pct_sum: data.total_pnl_pct,
|
|
72
|
+
position_count: data.count
|
|
124
73
|
};
|
|
125
74
|
}
|
|
126
75
|
}
|
|
127
|
-
|
|
128
76
|
return result;
|
|
129
77
|
}
|
|
130
|
-
|
|
78
|
+
|
|
131
79
|
reset() {
|
|
132
|
-
this.
|
|
133
|
-
// --- STANDARD 0: RENAMED ---
|
|
80
|
+
this.stockData.clear();
|
|
134
81
|
this.tickerMap = null;
|
|
135
82
|
}
|
|
136
83
|
}
|