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
|
@@ -7,17 +7,17 @@
|
|
|
7
7
|
* This is different from 'daily_asset_activity' because it counts
|
|
8
8
|
* *positions*, not *unique users*.
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class DailyBoughtVsSoldCount {
|
|
14
14
|
constructor() {
|
|
15
15
|
// We will store { [instrumentId]: { new: 0, closed: 0 } }
|
|
16
16
|
this.assetActivity = new Map();
|
|
17
|
-
|
|
17
|
+
// --- STANDARD 0: RENAMED ---
|
|
18
|
+
this.tickerMap = null;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
// --- NEW ---
|
|
21
21
|
/**
|
|
22
22
|
* Statically defines all metadata for the manifest builder.
|
|
23
23
|
*/
|
|
@@ -31,7 +31,6 @@ class DailyBoughtVsSoldCount {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// --- NEW ---
|
|
35
34
|
/**
|
|
36
35
|
* Statically declare dependencies.
|
|
37
36
|
*/
|
|
@@ -41,7 +40,6 @@ class DailyBoughtVsSoldCount {
|
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
42
|
* Defines the output schema for this calculation.
|
|
44
|
-
* @returns {object} JSON Schema object
|
|
45
43
|
*/
|
|
46
44
|
static getSchema() {
|
|
47
45
|
const tickerSchema = {
|
|
@@ -93,7 +91,13 @@ class DailyBoughtVsSoldCount {
|
|
|
93
91
|
return new Map(positions.map(p => [p.PositionID, p.InstrumentID]).filter(p => p[0] && p[1]));
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
// --- STANDARD 0: UPDATED SIGNATURE (added context) ---
|
|
95
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
96
|
+
// --- STANDARD 0: ADDED ---
|
|
97
|
+
if (!this.tickerMap) {
|
|
98
|
+
this.tickerMap = context.instrumentToTicker;
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
if (!todayPortfolio || !yesterdayPortfolio) {
|
|
98
102
|
return;
|
|
99
103
|
}
|
|
@@ -119,13 +123,17 @@ class DailyBoughtVsSoldCount {
|
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
async getResult() {
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
127
|
+
|
|
128
|
+
// Failsafe check
|
|
129
|
+
if (!this.tickerMap) {
|
|
130
|
+
return {}; // process() must run first
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
const result = {};
|
|
127
134
|
for (const [instrumentId, data] of this.assetActivity.entries()) {
|
|
128
|
-
|
|
135
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
136
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
129
137
|
|
|
130
138
|
const openCount = data.new;
|
|
131
139
|
const closeCount = data.closed;
|
|
@@ -143,7 +151,8 @@ class DailyBoughtVsSoldCount {
|
|
|
143
151
|
|
|
144
152
|
reset() {
|
|
145
153
|
this.assetActivity.clear();
|
|
146
|
-
|
|
154
|
+
// --- STANDARD 0: RENAMED ---
|
|
155
|
+
this.tickerMap = null;
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
|
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
* It runs ONCE, loads today's and yesterday's pre-aggregated 'insights' docs,
|
|
9
9
|
* and calculates the delta based on the 'total' field.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class DailyOwnershipDelta {
|
|
15
15
|
|
|
16
|
-
// ---
|
|
16
|
+
// --- STANDARD 2: ADDED ---
|
|
17
|
+
constructor() {
|
|
18
|
+
this.result = {};
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* Statically defines all metadata for the manifest builder.
|
|
19
23
|
*/
|
|
@@ -27,7 +31,6 @@ class DailyOwnershipDelta {
|
|
|
27
31
|
};
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
// --- NEW ---
|
|
31
34
|
/**
|
|
32
35
|
* Statically declare dependencies.
|
|
33
36
|
*/
|
|
@@ -37,7 +40,6 @@ class DailyOwnershipDelta {
|
|
|
37
40
|
|
|
38
41
|
/**
|
|
39
42
|
* Defines the output schema for this calculation.
|
|
40
|
-
* @returns {object} JSON Schema object
|
|
41
43
|
*/
|
|
42
44
|
static getSchema() {
|
|
43
45
|
const tickerSchema = {
|
|
@@ -76,20 +78,33 @@ class DailyOwnershipDelta {
|
|
|
76
78
|
|
|
77
79
|
/**
|
|
78
80
|
* This is a 'meta' and 'historical' calculation. It runs once.
|
|
79
|
-
* @param {string} dateStr - The date string 'YYYY-MM-DD'.
|
|
80
|
-
* @param {object} todayRootData - Root data for today. We expect todayRootData.insights.
|
|
81
|
-
* @param {object} yesterdayRootData - Root data for yesterday. We expect yesterdayRootData.insights.
|
|
82
|
-
* @returns {Promise<object>} The calculation result.
|
|
83
81
|
*/
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
// --- STANDARD 1: UPDATED SIGNATURE ---
|
|
83
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
84
|
+
|
|
85
|
+
// ---
|
|
86
|
+
// FIX: The test harness ('worker.js') is now our "ground truth".
|
|
87
|
+
//
|
|
88
|
+
// 1. Get Ticker Mappings:
|
|
89
|
+
// 'worker.js' passes the context object as ARGUMENT 4 (named 'config').
|
|
90
|
+
// This context *already contains* the ticker map.
|
|
91
|
+
// ---
|
|
92
|
+
const { instrumentToTicker } = config; // 'config' is Param 4
|
|
86
93
|
|
|
87
94
|
const todayOwners = new Map();
|
|
88
95
|
const yesterdayOwners = new Map();
|
|
89
96
|
const allInstrumentIds = new Set();
|
|
90
97
|
|
|
98
|
+
// ---
|
|
99
|
+
// 2. Get Data:
|
|
100
|
+
// 'worker.js' passes the root data object as ARGUMENT 1 (named 'dateStr').
|
|
101
|
+
// ---
|
|
102
|
+
const rootDataToday = dateStr; // 'dateStr' is Param 1
|
|
103
|
+
|
|
104
|
+
const todayDoc = rootDataToday?.insights;
|
|
105
|
+
const yesterdayDoc = rootDataToday?.yesterdayInsights;
|
|
106
|
+
|
|
91
107
|
// 1. Process today's insights doc
|
|
92
|
-
const todayDoc = todayRootData.insights;
|
|
93
108
|
if (todayDoc && Array.isArray(todayDoc.insights)) {
|
|
94
109
|
for (const instrument of todayDoc.insights) {
|
|
95
110
|
const id = instrument.instrumentId;
|
|
@@ -100,7 +115,6 @@ class DailyOwnershipDelta {
|
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
// 2. Process yesterday's insights doc
|
|
103
|
-
const yesterdayDoc = yesterdayRootData.insights;
|
|
104
118
|
if (yesterdayDoc && Array.isArray(yesterdayDoc.insights)) {
|
|
105
119
|
for (const instrument of yesterdayDoc.insights) {
|
|
106
120
|
const id = instrument.instrumentId;
|
|
@@ -113,7 +127,8 @@ class DailyOwnershipDelta {
|
|
|
113
127
|
// 3. Calculate deltas
|
|
114
128
|
const result = {};
|
|
115
129
|
for (const instrumentId of allInstrumentIds) {
|
|
116
|
-
|
|
130
|
+
// --- Use the map from Param 4 ---
|
|
131
|
+
const ticker = instrumentToTicker[instrumentId] || `id_${instrumentId}`;
|
|
117
132
|
|
|
118
133
|
const tOwners = todayOwners.get(instrumentId) || 0;
|
|
119
134
|
const yOwners = yesterdayOwners.get(instrumentId) || 0;
|
|
@@ -128,10 +143,19 @@ class DailyOwnershipDelta {
|
|
|
128
143
|
}
|
|
129
144
|
}
|
|
130
145
|
|
|
131
|
-
|
|
146
|
+
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
147
|
+
this.result = result;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --- STANDARD 2: ADDED ---
|
|
151
|
+
async getResult(fetchedDependencies) {
|
|
152
|
+
return this.result;
|
|
132
153
|
}
|
|
133
154
|
|
|
134
|
-
//
|
|
155
|
+
// --- STANDARD 2: ADDED ---
|
|
156
|
+
reset() {
|
|
157
|
+
this.result = {};
|
|
158
|
+
}
|
|
135
159
|
}
|
|
136
160
|
|
|
137
161
|
module.exports = DailyOwnershipDelta;
|
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
* and uses the sector mapping provider to aggregate the total number
|
|
7
7
|
* of owners for each sector.
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
10
10
|
|
|
11
11
|
class DailyOwnershipPerSector {
|
|
12
12
|
|
|
13
|
-
// ---
|
|
13
|
+
// --- STANDARD 2: ADDED ---
|
|
14
|
+
constructor() {
|
|
15
|
+
this.result = {};
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
/**
|
|
15
19
|
* Statically defines all metadata for the manifest builder.
|
|
16
20
|
*/
|
|
@@ -24,7 +28,6 @@ class DailyOwnershipPerSector {
|
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
// --- NEW ---
|
|
28
31
|
/**
|
|
29
32
|
* Statically declare dependencies.
|
|
30
33
|
*/
|
|
@@ -34,7 +37,6 @@ class DailyOwnershipPerSector {
|
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
39
|
* Defines the output schema for this calculation.
|
|
37
|
-
* @returns {object} JSON Schema object
|
|
38
40
|
*/
|
|
39
41
|
static getSchema() {
|
|
40
42
|
const sectorSchema = {
|
|
@@ -61,23 +63,31 @@ class DailyOwnershipPerSector {
|
|
|
61
63
|
|
|
62
64
|
/**
|
|
63
65
|
* This is a 'meta' calculation. It runs once.
|
|
64
|
-
* @param {string} dateStr - The date string 'YYYY-MM-DD'.
|
|
65
|
-
* @param {object} rootData - The root data object. We expect rootData.insights.
|
|
66
|
-
* @param {object} dependencies - The shared dependencies (e.g., logger).
|
|
67
|
-
* @returns {Promise<object>} The calculation result.
|
|
68
66
|
*/
|
|
69
|
-
|
|
67
|
+
// --- STANDARD 1: UPDATED SIGNATURE ---
|
|
68
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
70
69
|
// { [sectorName]: { total_owners: 0 } }
|
|
71
70
|
const sectorOwners = new Map();
|
|
72
71
|
|
|
73
|
-
//
|
|
74
|
-
|
|
72
|
+
// ---
|
|
73
|
+
// FIX: The test harness ('worker.js') is our "ground truth".
|
|
74
|
+
//
|
|
75
|
+
// 1. Get Logger & Mappings:
|
|
76
|
+
// 'worker.js' passes the context object as ARGUMENT 4 (named 'config').
|
|
77
|
+
// This context *already contains* the sector map.
|
|
78
|
+
// ---
|
|
79
|
+
const { logger, sectorMapping } = config; // 'config' is Param 4
|
|
75
80
|
|
|
76
81
|
// 2. Get the insights document
|
|
77
|
-
|
|
82
|
+
// 'worker.js' passes the root data object as ARGUMENT 1 (named 'dateStr').
|
|
83
|
+
// ---
|
|
84
|
+
const rootDataToday = dateStr; // 'dateStr' is Param 1
|
|
85
|
+
const insightsDoc = rootDataToday.insights;
|
|
86
|
+
|
|
78
87
|
if (!insightsDoc || !Array.isArray(insightsDoc.insights)) {
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
logger.log('WARN', `[daily-ownership-per-sector] No 'insights' data found.`);
|
|
89
|
+
this.result = {};
|
|
90
|
+
return;
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
// 3. Iterate over the pre-aggregated array
|
|
@@ -89,8 +99,8 @@ class DailyOwnershipPerSector {
|
|
|
89
99
|
continue;
|
|
90
100
|
}
|
|
91
101
|
|
|
92
|
-
//
|
|
93
|
-
const sectorName =
|
|
102
|
+
// --- Use the map from Param 4 ---
|
|
103
|
+
const sectorName = sectorMapping[instrumentId] || 'N/A';
|
|
94
104
|
|
|
95
105
|
// Initialize if new
|
|
96
106
|
if (!sectorOwners.has(sectorName)) {
|
|
@@ -101,8 +111,18 @@ class DailyOwnershipPerSector {
|
|
|
101
111
|
sectorOwners.get(sectorName).total_owners += totalOwners;
|
|
102
112
|
}
|
|
103
113
|
|
|
104
|
-
//
|
|
105
|
-
|
|
114
|
+
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
115
|
+
this.result = Object.fromEntries(sectorOwners);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// --- STANDARD 2: ADDED ---
|
|
119
|
+
async getResult(fetchedDependencies) {
|
|
120
|
+
return this.result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// --- STANDARD 2: ADDED ---
|
|
124
|
+
reset() {
|
|
125
|
+
this.result = {};
|
|
106
126
|
}
|
|
107
127
|
}
|
|
108
128
|
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
class DailyTotalPositionsHeld {
|
|
9
9
|
|
|
10
|
-
// ---
|
|
10
|
+
// --- STANDARD 2: ADDED ---
|
|
11
|
+
constructor() {
|
|
12
|
+
this.result = {};
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* Statically defines all metadata for the manifest builder.
|
|
13
17
|
*/
|
|
@@ -21,7 +25,6 @@ class DailyTotalPositionsHeld {
|
|
|
21
25
|
};
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
// --- NEW ---
|
|
25
28
|
/**
|
|
26
29
|
* Statically declare dependencies.
|
|
27
30
|
*/
|
|
@@ -31,7 +34,6 @@ class DailyTotalPositionsHeld {
|
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* Defines the output schema for this calculation.
|
|
34
|
-
* @returns {object} JSON Schema object
|
|
35
37
|
*/
|
|
36
38
|
static getSchema() {
|
|
37
39
|
return {
|
|
@@ -49,20 +51,31 @@ class DailyTotalPositionsHeld {
|
|
|
49
51
|
|
|
50
52
|
/**
|
|
51
53
|
* This is a 'meta' calculation. It runs once.
|
|
52
|
-
* @param {string} dateStr - The date string 'YYYY-MM-DD'.
|
|
53
|
-
* @param {object} rootData - The root data object. We expect rootData.insights.
|
|
54
|
-
* @param {object} dependencies - The shared dependencies (e.g., logger).
|
|
55
|
-
* @returns {object} The calculation result.
|
|
56
54
|
*/
|
|
57
|
-
|
|
55
|
+
// --- STANDARD 1: UPDATED SIGNATURE ---
|
|
56
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
58
57
|
let totalPositions = 0;
|
|
59
58
|
|
|
60
|
-
//
|
|
61
|
-
|
|
59
|
+
// ---
|
|
60
|
+
// FIX: The test harness ('worker.js') is our "ground truth".
|
|
61
|
+
//
|
|
62
|
+
// 1. Get Logger:
|
|
63
|
+
// 'worker.js' passes the context object as ARGUMENT 4 (named 'config').
|
|
64
|
+
// ---
|
|
65
|
+
const { logger } = config; // 'config' is Param 4
|
|
66
|
+
|
|
67
|
+
// ---
|
|
68
|
+
// 2. Get Data:
|
|
69
|
+
// 'worker.js' passes the root data object as ARGUMENT 1 (named 'dateStr').
|
|
70
|
+
// ---
|
|
71
|
+
const rootDataToday = dateStr; // 'dateStr' is Param 1
|
|
72
|
+
const insightsDoc = rootDataToday.insights;
|
|
62
73
|
|
|
63
74
|
if (!insightsDoc || !Array.isArray(insightsDoc.insights)) {
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
logger.log('WARN', `[daily-total-positions-held] No 'insights' data found.`);
|
|
76
|
+
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
77
|
+
this.result = { totalPositions: 0 };
|
|
78
|
+
return;
|
|
66
79
|
}
|
|
67
80
|
|
|
68
81
|
for (const instrument of insightsDoc.insights) {
|
|
@@ -72,12 +85,21 @@ class DailyTotalPositionsHeld {
|
|
|
72
85
|
}
|
|
73
86
|
}
|
|
74
87
|
|
|
75
|
-
|
|
88
|
+
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
89
|
+
this.result = {
|
|
76
90
|
totalPositions: totalPositions
|
|
77
91
|
};
|
|
78
92
|
}
|
|
79
93
|
|
|
80
|
-
//
|
|
94
|
+
// --- STANDARD 2: ADDED ---
|
|
95
|
+
async getResult(fetchedDependencies) {
|
|
96
|
+
return this.result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- STANDARD 2: ADDED ---
|
|
100
|
+
reset() {
|
|
101
|
+
this.result = {};
|
|
102
|
+
}
|
|
81
103
|
}
|
|
82
104
|
|
|
83
105
|
module.exports = DailyTotalPositionsHeld;
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
* and a 'stats' object containing these required values.
|
|
16
16
|
* ---------------------
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
19
19
|
|
|
20
20
|
// Define the P&L percentage buckets for the histogram
|
|
21
21
|
const BUCKETS = [
|
|
22
22
|
{ label: 'loss_heavy', min: -Infinity, max: -50 }, // > 50% loss
|
|
23
|
-
{ label: 'loss_medium', min: -50, max: -25 },
|
|
24
|
-
{ label: 'loss_light', min: -25, max: 0 },
|
|
25
|
-
{ label: 'gain_light', min: 0, max: 25 },
|
|
26
|
-
{ label: 'gain_medium', min: 25, max: 50 },
|
|
27
|
-
{ label: 'gain_heavy', min: 50, max: 100 },
|
|
23
|
+
{ label: 'loss_medium', min: -50, max: -25 }, // 25% to 50% loss
|
|
24
|
+
{ label: 'loss_light', min: -25, max: 0 }, // 0% to 25% loss
|
|
25
|
+
{ label: 'gain_light', min: 0, max: 25 }, // 0% to 25% gain
|
|
26
|
+
{ label: 'gain_medium', min: 25, max: 50 }, // 25% to 50% gain
|
|
27
|
+
{ label: 'gain_heavy', min: 50, max: 100 }, // 50% to 100% gain
|
|
28
28
|
{ label: 'gain_extreme', min: 100, max: Infinity } // > 100% gain
|
|
29
29
|
];
|
|
30
30
|
|
|
@@ -32,10 +32,10 @@ class PnlDistributionPerStock {
|
|
|
32
32
|
constructor() {
|
|
33
33
|
// We will store { [instrumentId]: [pnlPercent1, pnlPercent2, ...] }
|
|
34
34
|
this.pnlMap = new Map();
|
|
35
|
-
|
|
35
|
+
// --- STANDARD 0: RENAMED ---
|
|
36
|
+
this.tickerMap = null;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
// --- NEW ---
|
|
39
39
|
/**
|
|
40
40
|
* Statically defines all metadata for the manifest builder.
|
|
41
41
|
*/
|
|
@@ -49,7 +49,6 @@ class PnlDistributionPerStock {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// --- NEW ---
|
|
53
52
|
/**
|
|
54
53
|
* Statically declare dependencies.
|
|
55
54
|
*/
|
|
@@ -59,11 +58,6 @@ class PnlDistributionPerStock {
|
|
|
59
58
|
|
|
60
59
|
/**
|
|
61
60
|
* Defines the output schema for this calculation.
|
|
62
|
-
* REFACTOR: Schema now describes the server-calculated histogram.
|
|
63
|
-
*
|
|
64
|
-
* --- FIX: 2025-11-12 ---
|
|
65
|
-
* Added 'stats' object to the schema to support downstream
|
|
66
|
-
* meta-calculations like crowd_sharpe_ratio_proxy.
|
|
67
61
|
*/
|
|
68
62
|
static getSchema() {
|
|
69
63
|
const bucketSchema = {
|
|
@@ -115,52 +109,66 @@ class PnlDistributionPerStock {
|
|
|
115
109
|
}
|
|
116
110
|
}
|
|
117
111
|
|
|
118
|
-
// ---
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
// --- STANDARD 0: UPDATED SIGNATURE ---
|
|
113
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
114
|
+
// --- STANDARD 0: ADDED ---
|
|
115
|
+
if (!this.tickerMap) {
|
|
116
|
+
this.tickerMap = context.instrumentToTicker;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
|
|
122
120
|
if (!positions || !Array.isArray(positions)) {
|
|
123
121
|
return;
|
|
124
122
|
}
|
|
125
123
|
|
|
126
124
|
for (const pos of positions) {
|
|
127
125
|
const instrumentId = pos.InstrumentID;
|
|
128
|
-
const pnlPercent = pos.ProfitRate; // ProfitRate is P&L % (e.g., 0.5 = +50%)
|
|
129
126
|
|
|
130
|
-
//
|
|
127
|
+
// ---
|
|
128
|
+
// FIX 1: The schema file (schema.md) shows the P&L field
|
|
129
|
+
// is 'NetProfit', not 'ProfitRate'.
|
|
130
|
+
// ---
|
|
131
|
+
const pnlPercent = pos.NetProfit;
|
|
132
|
+
|
|
131
133
|
if (!instrumentId || typeof pnlPercent !== 'number') {
|
|
132
134
|
continue;
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
this._initAsset(instrumentId);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
|
|
139
|
+
// ---
|
|
140
|
+
// FIX 2: The 'NetProfit' field is already a full percentage
|
|
141
|
+
// (e.g., 34.09), not a decimal (e.g., 0.34).
|
|
142
|
+
// Do not multiply by 100.
|
|
143
|
+
// ---
|
|
144
|
+
this.pnlMap.get(instrumentId).push(pnlPercent);
|
|
138
145
|
}
|
|
139
146
|
}
|
|
140
147
|
|
|
141
148
|
/**
|
|
142
149
|
* REFACTOR: This method now calculates the distribution on the server.
|
|
143
|
-
* It transforms the raw P&L arrays into histogram bucket counts.
|
|
144
|
-
*
|
|
145
150
|
* --- FIX: 2025-11-12 ---
|
|
146
151
|
* Also calculates and returns sum, sumSq, and count.
|
|
147
152
|
*/
|
|
148
153
|
async getResult() {
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
155
|
+
|
|
156
|
+
// Failsafe check
|
|
157
|
+
if (!this.tickerMap) {
|
|
158
|
+
return {}; // process() must run first
|
|
151
159
|
}
|
|
152
160
|
|
|
153
161
|
const result = {};
|
|
154
162
|
|
|
155
163
|
for (const [instrumentId, pnlValues] of this.pnlMap.entries()) {
|
|
156
|
-
|
|
164
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
165
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
157
166
|
const count = pnlValues.length;
|
|
158
167
|
|
|
159
168
|
if (count === 0) {
|
|
160
169
|
continue;
|
|
161
170
|
}
|
|
162
171
|
|
|
163
|
-
// 1. Initialize the histogram object for this ticker
|
|
164
172
|
const histogram = {
|
|
165
173
|
loss_heavy: 0,
|
|
166
174
|
loss_medium: 0,
|
|
@@ -172,33 +180,27 @@ class PnlDistributionPerStock {
|
|
|
172
180
|
total_positions: count
|
|
173
181
|
};
|
|
174
182
|
|
|
175
|
-
// --- FIX: Initialize stats ---
|
|
176
183
|
let sum = 0;
|
|
177
184
|
let sumSq = 0;
|
|
178
185
|
|
|
179
|
-
// 2. Process all P&L values into buckets and calculate stats
|
|
180
186
|
for (const pnl of pnlValues) {
|
|
181
|
-
// --- FIX: Add to stats ---
|
|
182
187
|
sum += pnl;
|
|
183
188
|
sumSq += (pnl * pnl);
|
|
184
189
|
|
|
185
|
-
// Add to histogram
|
|
186
190
|
for (const bucket of BUCKETS) {
|
|
187
191
|
if (pnl >= bucket.min && pnl < bucket.max) {
|
|
188
192
|
histogram[bucket.label]++;
|
|
189
|
-
break;
|
|
193
|
+
break;
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
}
|
|
193
197
|
|
|
194
|
-
// --- FIX: Create stats object ---
|
|
195
198
|
const stats = {
|
|
196
199
|
sum: sum,
|
|
197
200
|
sumSq: sumSq,
|
|
198
201
|
count: count
|
|
199
202
|
};
|
|
200
203
|
|
|
201
|
-
// 3. Add the aggregated histogram and stats to the final result
|
|
202
204
|
result[ticker] = {
|
|
203
205
|
histogram: histogram,
|
|
204
206
|
stats: stats
|
|
@@ -209,7 +211,8 @@ class PnlDistributionPerStock {
|
|
|
209
211
|
|
|
210
212
|
reset() {
|
|
211
213
|
this.pnlMap.clear();
|
|
212
|
-
|
|
214
|
+
// --- STANDARD 0: RENAMED ---
|
|
215
|
+
this.tickerMap = null;
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
218
|
|