aiden-shared-calculations-unified 1.0.83 → 1.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/calculations/core/asset-pnl-status.js +122 -104
- package/calculations/core/asset-position-size.js +110 -73
- package/calculations/core/average-daily-pnl-all-users.js +17 -3
- package/calculations/core/average-daily-pnl-per-sector.js +83 -75
- package/calculations/core/average-daily-pnl-per-stock.js +84 -73
- package/calculations/core/average-daily-position-pnl.js +2 -2
- package/calculations/core/holding-duration-per-asset.js +24 -23
- package/calculations/core/instrument-price-change-1d.js +72 -82
- package/calculations/core/instrument-price-momentum-20d.js +66 -100
- package/calculations/core/long-position-per-stock.js +21 -13
- package/calculations/core/overall-holding-duration.js +8 -3
- package/calculations/core/overall-profitability-ratio.js +2 -2
- package/calculations/core/platform-buy-sell-sentiment.js +75 -22
- package/calculations/core/platform-daily-bought-vs-sold-count.js +19 -10
- package/calculations/core/platform-daily-ownership-delta.js +39 -15
- package/calculations/core/platform-ownership-per-sector.js +38 -18
- package/calculations/core/platform-total-positions-held.js +36 -14
- package/calculations/core/pnl-distribution-per-stock.js +39 -36
- package/calculations/core/price-metrics.js +70 -172
- package/calculations/core/profitability-ratio-per-sector.js +23 -29
- package/calculations/core/profitability-ratio-per-stock.js +20 -13
- package/calculations/core/profitability-skew-per-stock.js +20 -13
- package/calculations/core/profitable-and-unprofitable-status.js +34 -10
- package/calculations/core/sentiment-per-stock.js +20 -9
- package/calculations/core/short-position-per-stock.js +23 -37
- package/calculations/core/social-activity-aggregation.js +41 -115
- package/calculations/core/social-asset-posts-trend.js +77 -94
- package/calculations/core/social-event-correlation.js +87 -106
- package/calculations/core/social-sentiment-aggregation.js +56 -138
- package/calculations/core/social-top-mentioned-words.js +74 -106
- package/calculations/core/social-topic-interest-evolution.js +94 -94
- package/calculations/core/social-topic-sentiment-matrix.js +90 -74
- package/calculations/core/social-word-mentions-trend.js +92 -106
- package/calculations/core/speculator-asset-sentiment.js +63 -92
- package/calculations/core/speculator-danger-zone.js +77 -90
- package/calculations/core/speculator-distance-to-stop-loss-per-leverage.js +75 -90
- package/calculations/core/speculator-distance-to-tp-per-leverage.js +75 -88
- package/calculations/core/speculator-entry-distance-to-sl-per-leverage.js +75 -90
- package/calculations/core/speculator-entry-distance-to-tp-per-leverage.js +74 -89
- package/calculations/core/speculator-leverage-per-asset.js +62 -57
- package/calculations/core/speculator-leverage-per-sector.js +53 -65
- package/calculations/core/speculator-risk-reward-ratio-per-asset.js +71 -76
- package/calculations/core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js +60 -81
- package/calculations/core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js +57 -77
- package/calculations/core/speculator-stop-loss-per-asset.js +43 -80
- package/calculations/core/speculator-take-profit-per-asset.js +45 -69
- package/calculations/core/speculator-tsl-per-asset.js +42 -49
- package/calculations/core/total-long-figures.js +19 -19
- package/calculations/core/total-long-per-sector.js +39 -36
- package/calculations/core/total-short-figures.js +19 -19
- package/calculations/core/total-short-per-sector.js +39 -36
- package/calculations/core/users-processed.js +52 -25
- package/calculations/gauss/cohort-capital-flow.js +38 -29
- package/calculations/gauss/cohort-definer.js +17 -25
- package/calculations/gauss/daily-dna-filter.js +10 -4
- package/calculations/gauss/gauss-divergence-signal.js +28 -6
- package/calculations/gem/cohort-momentum-state.js +113 -92
- package/calculations/gem/cohort-skill-definition.js +23 -53
- package/calculations/gem/platform-conviction-divergence.js +62 -116
- package/calculations/gem/quant-skill-alpha-signal.js +107 -123
- package/calculations/gem/skilled-cohort-flow.js +178 -167
- package/calculations/gem/skilled-unskilled-divergence.js +73 -113
- package/calculations/gem/unskilled-cohort-flow.js +176 -166
- package/calculations/helix/helix-contrarian-signal.js +91 -83
- package/calculations/helix/herd-consensus-score.js +135 -97
- package/calculations/helix/winner-loser-flow.js +14 -16
- package/calculations/pyro/risk-appetite-index.js +121 -123
- package/calculations/pyro/squeeze-potential.js +93 -125
- package/calculations/pyro/volatility-signal.js +109 -97
- package/package.json +5 -5
- package/README.MD +0 -155
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for total users processed.
|
|
3
|
+
*
|
|
4
|
+
* This metric is a simple counter that tracks the total number
|
|
5
|
+
* of users processed and the breakdown by user type (Normal/Speculator).
|
|
6
|
+
*
|
|
7
|
+
* It is a fundamental metric for sanity-checking the data stream.
|
|
3
8
|
*/
|
|
4
|
-
|
|
5
9
|
class UsersProcessed {
|
|
6
10
|
constructor() {
|
|
7
|
-
this.
|
|
11
|
+
this.totalUsers = 0;
|
|
12
|
+
this.normalUsers = 0;
|
|
13
|
+
this.speculatorUsers = 0;
|
|
14
|
+
this.totalValue = 0;
|
|
8
15
|
}
|
|
9
16
|
|
|
10
|
-
// --- NEW ---
|
|
11
17
|
/**
|
|
12
18
|
* Statically defines all metadata for the manifest builder.
|
|
13
19
|
*/
|
|
14
20
|
static getMetadata() {
|
|
15
21
|
return {
|
|
16
22
|
type: 'standard',
|
|
17
|
-
rootDataDependencies: ['portfolio'], // Needs
|
|
23
|
+
rootDataDependencies: ['portfolio'], // Needs summary
|
|
18
24
|
isHistorical: false,
|
|
19
25
|
userType: 'all',
|
|
20
|
-
category: '
|
|
26
|
+
category: 'core_metrics' // A fundamental metric
|
|
21
27
|
};
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
// --- NEW ---
|
|
25
30
|
/**
|
|
26
31
|
* Statically declare dependencies.
|
|
27
32
|
*/
|
|
@@ -31,41 +36,63 @@ class UsersProcessed {
|
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Defines the output schema for this calculation.
|
|
34
|
-
* @returns {object} JSON Schema object
|
|
35
39
|
*/
|
|
36
40
|
static getSchema() {
|
|
37
41
|
return {
|
|
38
42
|
"type": "object",
|
|
39
|
-
"description": "
|
|
43
|
+
"description": "Tracks the total count of users processed and their type.",
|
|
40
44
|
"properties": {
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
+
"total_users": { "type": "number" },
|
|
46
|
+
"normal_users": { "type": "number" },
|
|
47
|
+
"speculator_users": { "type": "number" },
|
|
48
|
+
"total_portfolio_value": { "type": "number" }
|
|
45
49
|
},
|
|
46
|
-
"required": ["
|
|
50
|
+
"required": ["total_users", "normal_users", "speculator_users", "total_portfolio_value"]
|
|
47
51
|
};
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
// --- UPDATED SIGNATURE ---
|
|
55
|
+
process(
|
|
56
|
+
todayPortfolio,
|
|
57
|
+
yesterdayPortfolio,
|
|
58
|
+
userId,
|
|
59
|
+
context,
|
|
60
|
+
todayInsights,
|
|
61
|
+
yesterdayInsights,
|
|
62
|
+
todaySocialPostInsights,
|
|
63
|
+
yesterdaySocialPostInsights,
|
|
64
|
+
todayHistory
|
|
65
|
+
) {
|
|
66
|
+
this.totalUsers++;
|
|
67
|
+
|
|
68
|
+
// Use the userType from the context, which is set by the orchestrator
|
|
69
|
+
if (context.userType === 'speculator') {
|
|
70
|
+
this.speculatorUsers++;
|
|
71
|
+
} else {
|
|
72
|
+
this.normalUsers++;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// --- UPDATED ---
|
|
76
|
+
const portfolioValue = todayPortfolio?.Summary?.PortfolioValue;
|
|
77
|
+
if (typeof portfolioValue === 'number') {
|
|
78
|
+
this.totalValue += portfolioValue;
|
|
79
|
+
}
|
|
52
80
|
}
|
|
53
81
|
|
|
54
82
|
getResult() {
|
|
55
|
-
if (this.userCount === 0) {
|
|
56
|
-
// Even if 0, we should return the defined schema shape
|
|
57
|
-
return {
|
|
58
|
-
rawTotalUsersProcessed: 0
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
// FIX: Rename the key to use the 'raw' prefix for correct aggregation.
|
|
62
83
|
return {
|
|
63
|
-
|
|
84
|
+
total_users: this.totalUsers,
|
|
85
|
+
normal_users: this.normalUsers,
|
|
86
|
+
speculator_users: this.speculatorUsers,
|
|
87
|
+
total_portfolio_value: this.totalValue
|
|
64
88
|
};
|
|
65
89
|
}
|
|
66
90
|
|
|
67
91
|
reset() {
|
|
68
|
-
this.
|
|
92
|
+
this.totalUsers = 0;
|
|
93
|
+
this.normalUsers = 0;
|
|
94
|
+
this.speculatorUsers = 0;
|
|
95
|
+
this.totalValue = 0;
|
|
69
96
|
}
|
|
70
97
|
}
|
|
71
98
|
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
* - Added dependency on 'instrument-price-change-1d' (Pass 1 meta calc).
|
|
13
13
|
* - Price adjustment logic now uses the reliable 1-day price change
|
|
14
14
|
* from the new dependency.
|
|
15
|
+
* - **FIXED:** getResult() was wrapping the ticker map in an "assets" object,
|
|
16
|
+
* causing a schema validation failure in the test harness.
|
|
15
17
|
* --------------------------
|
|
16
18
|
*/
|
|
17
|
-
|
|
19
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class CohortCapitalFlow {
|
|
@@ -23,7 +25,8 @@ class CohortCapitalFlow {
|
|
|
23
25
|
this.cohortFlows = new Map();
|
|
24
26
|
// { [userId]: "cohortName" }
|
|
25
27
|
this.cohortMap = new Map();
|
|
26
|
-
|
|
28
|
+
// --- STANDARD 0: RENAMED ---
|
|
29
|
+
this.tickerMap = null;
|
|
27
30
|
this.dependenciesLoaded = false;
|
|
28
31
|
|
|
29
32
|
// --- NEW ---
|
|
@@ -35,7 +38,6 @@ class CohortCapitalFlow {
|
|
|
35
38
|
* Defines the output schema for this calculation.
|
|
36
39
|
*/
|
|
37
40
|
static getSchema() {
|
|
38
|
-
// ... (Schema remains unchanged) ...
|
|
39
41
|
const flowSchema = {
|
|
40
42
|
"type": "object",
|
|
41
43
|
"properties": {
|
|
@@ -68,8 +70,6 @@ class CohortCapitalFlow {
|
|
|
68
70
|
static getMetadata() {
|
|
69
71
|
return {
|
|
70
72
|
type: 'standard',
|
|
71
|
-
// --- REVISED ---
|
|
72
|
-
// Removed 'insights' as it's no longer needed for price.
|
|
73
73
|
rootDataDependencies: ['portfolio', 'history'],
|
|
74
74
|
isHistorical: true, // Needs T-1 portfolio for flow
|
|
75
75
|
userType: 'all',
|
|
@@ -83,13 +83,19 @@ class CohortCapitalFlow {
|
|
|
83
83
|
static getDependencies() {
|
|
84
84
|
return [
|
|
85
85
|
'cohort-definer', // from gauss (Pass 2)
|
|
86
|
-
// --- REVISED ---
|
|
87
86
|
'instrument-price-change-1d' // from core (Pass 1)
|
|
88
87
|
];
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
_getPortfolioPositions(portfolio) {
|
|
91
|
+
// FIX: Check for Normal User (AggregatedPositions) OR Speculator (PublicPositions)
|
|
92
|
+
if (portfolio?.AggregatedPositions) {
|
|
93
|
+
return portfolio.AggregatedPositions; // Normal User
|
|
94
|
+
}
|
|
95
|
+
if (portfolio?.PublicPositions) {
|
|
96
|
+
return portfolio.PublicPositions; // Speculator User
|
|
97
|
+
}
|
|
98
|
+
return [];
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
_initFlowData(cohortName, instrumentId) {
|
|
@@ -124,7 +130,6 @@ class CohortCapitalFlow {
|
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
// 2. Load Price Change Data
|
|
127
|
-
// --- REVISED ---
|
|
128
133
|
this.priceChangeMap = fetchedDependencies['instrument-price-change-1d'] || {};
|
|
129
134
|
|
|
130
135
|
this.dependenciesLoaded = true;
|
|
@@ -133,8 +138,9 @@ class CohortCapitalFlow {
|
|
|
133
138
|
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
134
139
|
this._loadDependencies(fetchedDependencies);
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
// --- STANDARD 0: FIXED ---
|
|
142
|
+
if (!this.tickerMap) {
|
|
143
|
+
this.tickerMap = context.instrumentToTicker;
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
const cohortName = this.cohortMap.get(userId);
|
|
@@ -153,11 +159,14 @@ class CohortCapitalFlow {
|
|
|
153
159
|
return; // Must have AggregatedPositions for both days
|
|
154
160
|
}
|
|
155
161
|
|
|
156
|
-
// --- REVISED ---
|
|
157
|
-
// We no longer need insightsMap
|
|
158
162
|
if (!this.priceChangeMap) {
|
|
159
163
|
return; // Cannot calculate price-adjusted flow
|
|
160
164
|
}
|
|
165
|
+
|
|
166
|
+
// --- STANDARD 0: Check must be done before use ---
|
|
167
|
+
if (!this.tickerMap) {
|
|
168
|
+
return; // Cannot map instruments to tickers
|
|
169
|
+
}
|
|
161
170
|
|
|
162
171
|
const yPosMap = new Map(yPos.map(p => [p.InstrumentID, p]));
|
|
163
172
|
const tPosMap = new Map(tPos.map(p => [p.InstrumentID, p]));
|
|
@@ -178,14 +187,12 @@ class CohortCapitalFlow {
|
|
|
178
187
|
if (yInvested > 0) {
|
|
179
188
|
asset.total_invested_yesterday += yInvested;
|
|
180
189
|
|
|
181
|
-
// ---
|
|
182
|
-
|
|
183
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId];
|
|
190
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
191
|
+
const ticker = this.tickerMap[instrumentId];
|
|
184
192
|
const yPriceChange_pct = (ticker && this.priceChangeMap[ticker])
|
|
185
193
|
? this.priceChangeMap[ticker].price_change_1d_pct
|
|
186
194
|
: 0;
|
|
187
195
|
|
|
188
|
-
// Convert from percentage (5.5) to decimal (0.055)
|
|
189
196
|
const yPriceChange_decimal = (yPriceChange_pct || 0) / 100.0;
|
|
190
197
|
|
|
191
198
|
asset.price_change_yesterday += yPriceChange_decimal * yInvested; // Weighted sum
|
|
@@ -196,18 +203,22 @@ class CohortCapitalFlow {
|
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
205
|
|
|
206
|
+
// --- THIS IS THE FIX ---
|
|
199
207
|
async getResult() {
|
|
200
|
-
|
|
201
|
-
|
|
208
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
209
|
+
|
|
210
|
+
// Failsafe check
|
|
211
|
+
if (!this.tickerMap) {
|
|
212
|
+
return {}; // process() must run first to set mappings
|
|
202
213
|
}
|
|
203
214
|
|
|
204
215
|
const finalResult = {};
|
|
205
216
|
|
|
206
217
|
for (const [cohortName, assetMap] of this.cohortFlows.entries()) {
|
|
207
|
-
// --- REVISED: Initialize cohortAssets as an object ---
|
|
208
218
|
const cohortAssets = {};
|
|
209
219
|
for (const [instrumentId, data] of assetMap.entries()) {
|
|
210
|
-
|
|
220
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
221
|
+
const ticker = this.tickerMap[instrumentId];
|
|
211
222
|
if (!ticker) continue;
|
|
212
223
|
|
|
213
224
|
const { total_invested_yesterday, total_invested_today, price_change_yesterday } = data;
|
|
@@ -231,23 +242,21 @@ class CohortCapitalFlow {
|
|
|
231
242
|
};
|
|
232
243
|
}
|
|
233
244
|
}
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
finalResult[cohortName] = { assets: cohortAssets };
|
|
245
|
+
// Was: finalResult[cohortName] = { assets: cohortAssets };
|
|
246
|
+
// Is:
|
|
247
|
+
finalResult[cohortName] = cohortAssets;
|
|
238
248
|
}
|
|
239
249
|
|
|
240
|
-
// --- REVISED: The schema for this calc shows the output is NOT sharded. ---
|
|
241
|
-
// The return should be the final object.
|
|
242
250
|
return finalResult;
|
|
243
251
|
}
|
|
252
|
+
// --- END FIX ---
|
|
244
253
|
|
|
245
254
|
reset() {
|
|
246
255
|
this.cohortFlows.clear();
|
|
247
256
|
this.cohortMap.clear();
|
|
248
|
-
|
|
257
|
+
// --- STANDARD 0: RENAMED ---
|
|
258
|
+
this.tickerMap = null;
|
|
249
259
|
this.dependenciesLoaded = false;
|
|
250
|
-
// --- NEW ---
|
|
251
260
|
this.priceChangeMap = null;
|
|
252
261
|
}
|
|
253
262
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* It then buckets these users into our final, named sub-cohorts
|
|
9
9
|
* (e.g., "Smart Investors", "FOMO Chasers").
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class CohortDefiner {
|
|
@@ -18,7 +18,8 @@ class CohortDefiner {
|
|
|
18
18
|
this.dumbVectors = []; // [ { userId, skill, time, fomo, bagholder }, ... ]
|
|
19
19
|
|
|
20
20
|
this.momentumData = null;
|
|
21
|
-
|
|
21
|
+
// --- STANDARD 0: RENAMED ---
|
|
22
|
+
this.tickerMap = null;
|
|
22
23
|
this.cohortIdSets = null; // { smart: Set, dumb: Set }
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -60,7 +61,6 @@ class CohortDefiner {
|
|
|
60
61
|
"fomo_chasers": cohortSchema,
|
|
61
62
|
"patient_losers": cohortSchema,
|
|
62
63
|
"fomo_bagholders": cohortSchema,
|
|
63
|
-
// --- FIX [PROBLEM 9]: Add uncategorized bucket ---
|
|
64
64
|
"uncategorized_smart": cohortSchema,
|
|
65
65
|
"uncategorized_dumb": cohortSchema
|
|
66
66
|
},
|
|
@@ -77,14 +77,12 @@ class CohortDefiner {
|
|
|
77
77
|
const dnaFilterData = fetchedDependencies['daily-dna-filter'];
|
|
78
78
|
this.momentumData = fetchedDependencies['instrument-price-momentum-20d'];
|
|
79
79
|
|
|
80
|
-
// --- FIX [PROBLEM 6]: Validate dependency content, not just existence ---
|
|
81
80
|
if (dnaFilterData && dnaFilterData.smart_cohort_ids && dnaFilterData.dumb_cohort_ids) {
|
|
82
81
|
this.cohortIdSets = {
|
|
83
82
|
smart: new Set(dnaFilterData.smart_cohort_ids),
|
|
84
83
|
dumb: new Set(dnaFilterData.dumb_cohort_ids)
|
|
85
84
|
};
|
|
86
85
|
} else {
|
|
87
|
-
// Initialize with empty sets if dependency is missing or malformed
|
|
88
86
|
this.cohortIdSets = {
|
|
89
87
|
smart: new Set(),
|
|
90
88
|
dumb: new Set()
|
|
@@ -93,9 +91,9 @@ class CohortDefiner {
|
|
|
93
91
|
}
|
|
94
92
|
|
|
95
93
|
_getFomoScore(todayPortfolio, yesterdayPortfolio) {
|
|
96
|
-
|
|
94
|
+
// --- STANDARD 0: UPDATED CHECK ---
|
|
95
|
+
if (!this.tickerMap) return 0;
|
|
97
96
|
|
|
98
|
-
// --- FIX [PROBLEM 4 related]: Ensure momentum data is loaded ---
|
|
99
97
|
if (!this.momentumData) {
|
|
100
98
|
return 0; // Cannot calculate FOMO without momentum data
|
|
101
99
|
}
|
|
@@ -107,8 +105,8 @@ class CohortDefiner {
|
|
|
107
105
|
let fomoSum = 0;
|
|
108
106
|
let count = 0;
|
|
109
107
|
for (const pos of newPositions) {
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
109
|
+
const ticker = this.tickerMap[pos.InstrumentID];
|
|
112
110
|
if (ticker && this.momentumData[ticker]) {
|
|
113
111
|
fomoSum += this.momentumData[ticker].momentum_20d_pct || 0;
|
|
114
112
|
count++;
|
|
@@ -118,7 +116,8 @@ class CohortDefiner {
|
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
_getBagholderScore(todayPortfolio) {
|
|
121
|
-
|
|
119
|
+
// FIX: Only Speculator data (PublicPositions) has the OpenDateTime field needed for this calculation.
|
|
120
|
+
const openPositions = todayPortfolio?.PublicPositions || [];
|
|
122
121
|
if (openPositions.length === 0) return 0;
|
|
123
122
|
|
|
124
123
|
let durationSum = 0;
|
|
@@ -126,8 +125,7 @@ class CohortDefiner {
|
|
|
126
125
|
const now = new Date();
|
|
127
126
|
|
|
128
127
|
for (const pos of openPositions) {
|
|
129
|
-
|
|
130
|
-
if (pos.NetProfit < -0.20) {
|
|
128
|
+
if (pos.NetProfit < -20) {
|
|
131
129
|
try {
|
|
132
130
|
const openDate = new Date(pos.OpenDateTime);
|
|
133
131
|
const durationMs = now.getTime() - openDate.getTime();
|
|
@@ -144,8 +142,10 @@ class CohortDefiner {
|
|
|
144
142
|
|
|
145
143
|
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
146
144
|
this._loadDependencies(fetchedDependencies);
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
|
|
146
|
+
// --- STANDARD 0: FIXED ---
|
|
147
|
+
if (!this.tickerMap) {
|
|
148
|
+
this.tickerMap = context.instrumentToTicker;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
const isSmart = this.cohortIdSets.smart.has(userId);
|
|
@@ -155,9 +155,7 @@ class CohortDefiner {
|
|
|
155
155
|
return; // Not in our extreme cohorts
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
// 1. Get Long-Term (Stable) Traits
|
|
158
|
+
// FIX: Access history data from the nested property provided by the fixed worker.js
|
|
161
159
|
const history = todayPortfolio?.history?.all;
|
|
162
160
|
if (!history) return;
|
|
163
161
|
|
|
@@ -168,7 +166,6 @@ class CohortDefiner {
|
|
|
168
166
|
const lt_skill = (winRate * avgWin) - (lossRate * avgLoss);
|
|
169
167
|
const lt_time = history.avgHoldingTimeInMinutes || 0;
|
|
170
168
|
|
|
171
|
-
// 2. Get Short-Term (Noisy) Traits
|
|
172
169
|
const st_fomo = this._getFomoScore(todayPortfolio, yesterdayPortfolio);
|
|
173
170
|
const st_bagholder = this._getBagholderScore(todayPortfolio);
|
|
174
171
|
|
|
@@ -185,7 +182,6 @@ class CohortDefiner {
|
|
|
185
182
|
if (vectors.length === 0) return 0;
|
|
186
183
|
const sorted = vectors.map(v => v[key]).sort((a, b) => a - b);
|
|
187
184
|
const mid = Math.floor(sorted.length / 2);
|
|
188
|
-
// Handle even-length array by taking average of middle two
|
|
189
185
|
if (sorted.length % 2 === 0 && sorted.length > 0) {
|
|
190
186
|
return (sorted[mid - 1] + sorted[mid]) / 2;
|
|
191
187
|
}
|
|
@@ -197,7 +193,6 @@ class CohortDefiner {
|
|
|
197
193
|
const assignedSmart = new Set();
|
|
198
194
|
const assignedDumb = new Set();
|
|
199
195
|
|
|
200
|
-
// 1. Process Smart Cohort
|
|
201
196
|
const smart_median_time = this._getMedian(this.smartVectors, 'time');
|
|
202
197
|
|
|
203
198
|
cohorts['smart_investors'] = this.smartVectors
|
|
@@ -208,12 +203,10 @@ class CohortDefiner {
|
|
|
208
203
|
.filter(u => u.time < smart_median_time)
|
|
209
204
|
.map(u => { assignedSmart.add(u.userId); return u.userId; });
|
|
210
205
|
|
|
211
|
-
// --- FIX [PROBLEM 9]: Add uncategorized bucket ---
|
|
212
206
|
cohorts['uncategorized_smart'] = this.smartVectors
|
|
213
207
|
.filter(u => !assignedSmart.has(u.userId))
|
|
214
208
|
.map(u => u.userId);
|
|
215
209
|
|
|
216
|
-
// 2. Process Dumb Cohort
|
|
217
210
|
const dumb_median_fomo = this._getMedian(this.dumbVectors, 'fomo');
|
|
218
211
|
const dumb_median_bag = this._getMedian(this.dumbVectors, 'bagholder');
|
|
219
212
|
|
|
@@ -229,12 +222,10 @@ class CohortDefiner {
|
|
|
229
222
|
.filter(u => u.fomo >= dumb_median_fomo && u.bagholder >= dumb_median_bag)
|
|
230
223
|
.map(u => { assignedDumb.add(u.userId); return u.userId; });
|
|
231
224
|
|
|
232
|
-
// --- FIX [PROBLEM 9]: Add uncategorized bucket ---
|
|
233
225
|
cohorts['uncategorized_dumb'] = this.dumbVectors
|
|
234
226
|
.filter(u => !assignedDumb.has(u.userId))
|
|
235
227
|
.map(u => u.userId);
|
|
236
228
|
|
|
237
|
-
// Output is a compact map of cohort_name -> [userIds]
|
|
238
229
|
return cohorts;
|
|
239
230
|
}
|
|
240
231
|
|
|
@@ -242,7 +233,8 @@ class CohortDefiner {
|
|
|
242
233
|
this.smartVectors = [];
|
|
243
234
|
this.dumbVectors = [];
|
|
244
235
|
this.momentumData = null;
|
|
245
|
-
|
|
236
|
+
// --- STANDARD 0: RENAMED ---
|
|
237
|
+
this.tickerMap = null;
|
|
246
238
|
this.cohortIdSets = null;
|
|
247
239
|
}
|
|
248
240
|
}
|
|
@@ -21,8 +21,9 @@ class DailyDnaFilter {
|
|
|
21
21
|
static getMetadata() {
|
|
22
22
|
return {
|
|
23
23
|
type: 'standard',
|
|
24
|
-
// This calc only needs the 'history' doc
|
|
25
|
-
//
|
|
24
|
+
// This calc only needs the 'history' doc.
|
|
25
|
+
// As per the test harness, this means 'todayPortfolio'
|
|
26
|
+
// in process() *is* the history object.
|
|
26
27
|
rootDataDependencies: ['history'],
|
|
27
28
|
isHistorical: false, // Reads today's history snapshot
|
|
28
29
|
userType: 'all',
|
|
@@ -70,8 +71,13 @@ class DailyDnaFilter {
|
|
|
70
71
|
* @param {string} userId - The user's ID.
|
|
71
72
|
*/
|
|
72
73
|
process(todayPortfolio, yesterdayPortfolio, userId) {
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
|
|
75
|
+
// ---
|
|
76
|
+
// FIX: 'todayPortfolio' *is* the history object because
|
|
77
|
+
// getMetadata() only requests ['history'].
|
|
78
|
+
// The path is 'todayPortfolio.all', not 'todayPortfolio.history.all'.
|
|
79
|
+
// ---
|
|
80
|
+
const history = todayPortfolio?.all;
|
|
75
81
|
if (!history || !history.totalTrades || history.totalTrades < 20) {
|
|
76
82
|
return; // Not enough history for a reliable score
|
|
77
83
|
}
|
|
@@ -6,9 +6,20 @@
|
|
|
6
6
|
*
|
|
7
7
|
* It consumes the daily flows from all defined cohorts (Pass 3)
|
|
8
8
|
* and aggregates them into a final, actionable signal.
|
|
9
|
+
*
|
|
10
|
+
* --- FIX ---
|
|
11
|
+
* - Aligned data access with the corrected schema of its
|
|
12
|
+
* dependency, 'cohort-capital-flow'.
|
|
13
|
+
* - It now reads 'cohortFlows[cohortName]' directly, instead of
|
|
14
|
+
* the non-existent 'cohortFlows[cohortName].assets'.
|
|
9
15
|
*/
|
|
10
16
|
class GaussDivergenceSignal {
|
|
11
17
|
|
|
18
|
+
// --- STANDARD 2: ADDED ---
|
|
19
|
+
constructor() {
|
|
20
|
+
this.result = {};
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
/**
|
|
13
24
|
* Defines the output schema for this calculation.
|
|
14
25
|
* @returns {object} JSON Schema object
|
|
@@ -84,14 +95,16 @@ class GaussDivergenceSignal {
|
|
|
84
95
|
/**
|
|
85
96
|
* This is a 'meta' calculation. It runs once.
|
|
86
97
|
*/
|
|
87
|
-
|
|
98
|
+
// --- THIS IS THE FIX ---
|
|
99
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
88
100
|
const { logger } = dependencies;
|
|
89
101
|
|
|
90
102
|
const cohortFlows = fetchedDependencies['cohort-capital-flow'];
|
|
91
103
|
|
|
92
104
|
if (!cohortFlows) {
|
|
93
105
|
logger.log('WARN', `[gauss/gauss-divergence-signal] Missing dependency 'cohort-capital-flow' for ${dateStr}.`);
|
|
94
|
-
|
|
106
|
+
this.result = {};
|
|
107
|
+
return;
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
const SMART_COHORTS = ['smart_investors', 'smart_scalpers'];
|
|
@@ -105,9 +118,8 @@ class GaussDivergenceSignal {
|
|
|
105
118
|
const isDumb = DUMB_COHORTS.includes(cohortName);
|
|
106
119
|
if (!isSmart && !isDumb) continue;
|
|
107
120
|
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
const assets = cohortFlows[cohortName]?.assets;
|
|
121
|
+
// Was: const assets = cohortFlows[cohortName]?.assets;
|
|
122
|
+
const assets = cohortFlows[cohortName]; // This is now the ticker map
|
|
111
123
|
if (!assets) continue;
|
|
112
124
|
|
|
113
125
|
for (const [ticker, data] of Object.entries(assets)) {
|
|
@@ -115,6 +127,7 @@ class GaussDivergenceSignal {
|
|
|
115
127
|
blendedFlows.set(ticker, { smart: 0, dumb: 0 });
|
|
116
128
|
}
|
|
117
129
|
|
|
130
|
+
// This logic remains correct, as it reads the inner flow object
|
|
118
131
|
const flow = data.net_flow_contribution || 0;
|
|
119
132
|
|
|
120
133
|
if (isSmart) {
|
|
@@ -124,6 +137,7 @@ class GaussDivergenceSignal {
|
|
|
124
137
|
}
|
|
125
138
|
}
|
|
126
139
|
}
|
|
140
|
+
// --- END FIX ---
|
|
127
141
|
|
|
128
142
|
// 2. Calculate final signal (logic unchanged)
|
|
129
143
|
const result = {};
|
|
@@ -145,7 +159,15 @@ class GaussDivergenceSignal {
|
|
|
145
159
|
};
|
|
146
160
|
}
|
|
147
161
|
|
|
148
|
-
|
|
162
|
+
this.result = result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async getResult(fetchedDependencies) {
|
|
166
|
+
return this.result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
reset() {
|
|
170
|
+
this.result = {};
|
|
149
171
|
}
|
|
150
172
|
}
|
|
151
173
|
|