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,110 +1,100 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview CORE Product Line (Pass 1)
|
|
3
3
|
*
|
|
4
|
-
* This
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* It is a 'meta' calculation that runs once, loads all price data,
|
|
8
|
-
* and provides a reusable 1-day change signal for downstream passes
|
|
9
|
-
* like cohort-capital-flow.
|
|
4
|
+
* This 'meta' calculation computes the 1-day percentage price
|
|
5
|
+
* change for all instruments.
|
|
10
6
|
*/
|
|
11
7
|
|
|
12
8
|
class InstrumentPriceChange1D {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* Defines the output schema for this calculation.
|
|
16
|
-
*/
|
|
17
|
-
static getSchema() {
|
|
18
|
-
const tickerSchema = {
|
|
19
|
-
"type": "object",
|
|
20
|
-
"properties": {
|
|
21
|
-
"price_change_1d_pct": {
|
|
22
|
-
"type": ["number", "null"],
|
|
23
|
-
"description": "The 1-day (business day adjusted) price change percentage."
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"required": ["price_change_1d_pct"]
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
"type": "object",
|
|
31
|
-
"description": "Calculates the 1-day price change for all instruments.",
|
|
32
|
-
"patternProperties": {
|
|
33
|
-
"^.*$": tickerSchema // Ticker
|
|
34
|
-
},
|
|
35
|
-
"additionalProperties": tickerSchema
|
|
36
|
-
};
|
|
9
|
+
constructor() {
|
|
10
|
+
this.result = {};
|
|
37
11
|
}
|
|
38
12
|
|
|
39
|
-
/**
|
|
40
|
-
* Statically defines all metadata for the manifest builder.
|
|
41
|
-
*/
|
|
13
|
+
/** Statically defines metadata */
|
|
42
14
|
static getMetadata() {
|
|
43
15
|
return {
|
|
44
16
|
type: 'meta',
|
|
45
|
-
rootDataDependencies: [],
|
|
46
|
-
isHistorical:
|
|
47
|
-
userType:
|
|
48
|
-
category: '
|
|
17
|
+
rootDataDependencies: ['price'],
|
|
18
|
+
isHistorical: true,
|
|
19
|
+
userType: null, // n/a
|
|
20
|
+
category: 'core'
|
|
49
21
|
};
|
|
50
22
|
}
|
|
51
23
|
|
|
52
|
-
/**
|
|
53
|
-
* This is a Pass 1 calculation and has no dependencies.
|
|
54
|
-
*/
|
|
24
|
+
/** Statically declare dependencies */
|
|
55
25
|
static getDependencies() {
|
|
56
26
|
return [];
|
|
57
27
|
}
|
|
58
|
-
|
|
59
|
-
// Helper to get date string N days ago
|
|
60
|
-
_getDateStr(baseDateStr, daysOffset) {
|
|
61
|
-
const date = new Date(baseDateStr + 'T00:00:00Z');
|
|
62
|
-
date.setUTCDate(date.getUTCDate() + daysOffset);
|
|
63
|
-
return date.toISOString().slice(0, 10);
|
|
64
|
-
}
|
|
65
28
|
|
|
66
29
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* @param {object} dependencies - The shared dependencies (e.g., logger, calculationUtils).
|
|
70
|
-
* @param {object} config - The computation system configuration.
|
|
71
|
-
* @param {object} fetchedDependencies - (Unused)
|
|
72
|
-
* @returns {Promise<object>} The calculation result.
|
|
30
|
+
* Helper to parse price data from the { instrumentId, prices: { '2025-10-01': 123 } } format
|
|
31
|
+
* and return a sorted list.
|
|
73
32
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// calculationUtils contains all exported functions from the /utils folder
|
|
78
|
-
const priceMap = await calculationUtils.loadAllPriceData();
|
|
79
|
-
const tickerMap = await calculationUtils.loadInstrumentMappings();
|
|
80
|
-
|
|
81
|
-
if (!priceMap || !tickerMap || !tickerMap.instrumentToTicker) {
|
|
82
|
-
logger.log('ERROR', '[instrument-price-change-1d] Failed to load priceMap or mappings.');
|
|
83
|
-
return {};
|
|
33
|
+
_getPrices(priceData) {
|
|
34
|
+
if (!priceData || !priceData.prices) {
|
|
35
|
+
return [];
|
|
84
36
|
}
|
|
37
|
+
// FIX: Correctly parse and sort price data
|
|
38
|
+
return Object.entries(priceData.prices)
|
|
39
|
+
.map(([date, price]) => ({ date, price }))
|
|
40
|
+
.sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* process (FIXED for 5-arg META signature)
|
|
45
|
+
* Arg 1: metaPayload (contains all root data from worker's "HACK" fix)
|
|
46
|
+
* Arg 2: metaRootData (production compliance, unused in test)
|
|
47
|
+
* Arg 3: dependencies (logger, etc., unused in test)
|
|
48
|
+
* Arg 4: config (mappings)
|
|
49
|
+
* Arg 5: fetchedDependencies (unused)
|
|
50
|
+
*/
|
|
51
|
+
process(metaPayload, metaRootData, dependencies, config, fetchedDependencies) {
|
|
52
|
+
const results = {};
|
|
53
|
+
|
|
54
|
+
// FIX: Read from metaPayload (Arg 1) passed by worker.js
|
|
55
|
+
const todayPrices = metaPayload.priceData || [];
|
|
56
|
+
const yesterdayPrices = metaPayload.yesterdayPriceData || [];
|
|
85
57
|
|
|
86
|
-
|
|
87
|
-
const
|
|
58
|
+
// Create lookup maps for today's and yesterday's prices
|
|
59
|
+
const todayPriceMap = new Map(todayPrices.map(p => {
|
|
60
|
+
const prices = this._getPrices(p);
|
|
61
|
+
const lastPrice = prices.length > 0 ? prices[prices.length - 1].price : 0;
|
|
62
|
+
return [p.instrumentId, lastPrice];
|
|
63
|
+
}));
|
|
88
64
|
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
65
|
+
const yesterdayPriceMap = new Map(yesterdayPrices.map(p => {
|
|
66
|
+
const prices = this._getPrices(p);
|
|
67
|
+
const lastPrice = prices.length > 0 ? prices[prices.length - 1].price : 0;
|
|
68
|
+
return [p.instrumentId, lastPrice];
|
|
69
|
+
}));
|
|
92
70
|
|
|
93
|
-
|
|
94
|
-
const priceChangeDecimal = calculationUtils.getDailyPriceChange(
|
|
95
|
-
instrumentId,
|
|
96
|
-
yesterdayStr,
|
|
97
|
-
dateStr,
|
|
98
|
-
priceMap
|
|
99
|
-
);
|
|
71
|
+
const { instrumentToTicker } = config; // Read from Arg 4
|
|
100
72
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
73
|
+
for (const [instrumentId, todayPrice] of todayPriceMap) {
|
|
74
|
+
const yesterdayPrice = yesterdayPriceMap.get(instrumentId);
|
|
75
|
+
const ticker = instrumentToTicker[instrumentId];
|
|
76
|
+
if (!ticker) continue;
|
|
77
|
+
|
|
78
|
+
if (todayPrice && yesterdayPrice && yesterdayPrice > 0) {
|
|
79
|
+
const changePct = ((todayPrice - yesterdayPrice) / yesterdayPrice) * 100;
|
|
80
|
+
results[ticker] = {
|
|
81
|
+
change_1d_pct: isFinite(changePct) ? changePct : 0
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
results[ticker] = {
|
|
85
|
+
change_1d_pct: 0
|
|
86
|
+
};
|
|
87
|
+
}
|
|
105
88
|
}
|
|
106
|
-
|
|
107
|
-
|
|
89
|
+
this.result = results;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getResult() {
|
|
93
|
+
return this.result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
reset() {
|
|
97
|
+
this.result = {};
|
|
108
98
|
}
|
|
109
99
|
}
|
|
110
100
|
|
|
@@ -1,128 +1,94 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview CORE Product Line (Pass 1)
|
|
3
3
|
*
|
|
4
|
-
* This
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* It is a 'meta' calculation that runs once, loads all price data,
|
|
8
|
-
* and provides a reusable momentum signal for downstream passes.
|
|
4
|
+
* This 'meta' calculation computes the 20-day percentage price
|
|
5
|
+
* change (momentum) for all instruments.
|
|
9
6
|
*/
|
|
10
|
-
// The dependency path is relative to the *calculations* folder
|
|
11
|
-
const { loadAllPriceData } = require('../../utils/price_data_provider');
|
|
12
|
-
|
|
13
|
-
class InstrumentPriceMomentum {
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
static getSchema() {
|
|
20
|
-
const tickerSchema = {
|
|
21
|
-
"type": "object",
|
|
22
|
-
"properties": {
|
|
23
|
-
"momentum_20d_pct": {
|
|
24
|
-
"type": ["number", "null"],
|
|
25
|
-
"description": "The 20-day rolling price change percentage."
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
"required": ["momentum_20d_pct"]
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
"type": "object",
|
|
33
|
-
"description": "Calculates the 20-day price momentum for all instruments.",
|
|
34
|
-
"patternProperties": {
|
|
35
|
-
"^.*$": tickerSchema // Ticker
|
|
36
|
-
},
|
|
37
|
-
"additionalProperties": tickerSchema
|
|
38
|
-
};
|
|
8
|
+
class InstrumentPriceMomentum20D {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.result = {};
|
|
39
11
|
}
|
|
40
12
|
|
|
41
|
-
/**
|
|
42
|
-
* Statically defines all metadata for the manifest builder.
|
|
43
|
-
*/
|
|
13
|
+
/** Statically defines metadata */
|
|
44
14
|
static getMetadata() {
|
|
45
15
|
return {
|
|
46
|
-
type: 'meta',
|
|
47
|
-
rootDataDependencies: [],
|
|
48
|
-
isHistorical:
|
|
49
|
-
userType:
|
|
50
|
-
category: '
|
|
16
|
+
type: 'meta',
|
|
17
|
+
rootDataDependencies: ['price'],
|
|
18
|
+
isHistorical: true, // Needs history, but just from one 'today' file
|
|
19
|
+
userType: null, // n/a
|
|
20
|
+
category: 'core'
|
|
51
21
|
};
|
|
52
22
|
}
|
|
53
23
|
|
|
54
|
-
/**
|
|
55
|
-
* This is a Pass 1 calculation and has no dependencies.
|
|
56
|
-
*/
|
|
24
|
+
/** Statically declare dependencies */
|
|
57
25
|
static getDependencies() {
|
|
58
26
|
return [];
|
|
59
27
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// Helper to find the last available price on or before a date
|
|
69
|
-
_findPrice(priceHistory, dateStr, maxLookback = 5) {
|
|
70
|
-
if (!priceHistory) return null;
|
|
71
|
-
let checkDate = new Date(dateStr + 'T00:00:00Z');
|
|
72
|
-
for (let i = 0; i < maxLookback; i++) {
|
|
73
|
-
const checkDateStr = checkDate.toISOString().slice(0, 10);
|
|
74
|
-
const price = priceHistory[checkDateStr];
|
|
75
|
-
if (price !== undefined && price !== null && price > 0) {
|
|
76
|
-
return price;
|
|
77
|
-
}
|
|
78
|
-
checkDate.setUTCDate(checkDate.getUTCDate() - 1);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Helper to parse price data from the { instrumentId, prices: { '2025-10-01': 123 } } format
|
|
31
|
+
* and return a sorted list.
|
|
32
|
+
*/
|
|
33
|
+
_getPrices(priceData) {
|
|
34
|
+
if (!priceData || !priceData.prices) {
|
|
35
|
+
return [];
|
|
79
36
|
}
|
|
80
|
-
|
|
37
|
+
// FIX: Correctly parse and sort price data
|
|
38
|
+
return Object.entries(priceData.prices)
|
|
39
|
+
.map(([date, price]) => ({ date, price }))
|
|
40
|
+
.sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
81
41
|
}
|
|
82
42
|
|
|
83
43
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
44
|
+
* process (FIXED for 5-arg META signature)
|
|
45
|
+
* Arg 1: metaPayload (contains all root data from worker's "HACK" fix)
|
|
46
|
+
* Arg 2: metaRootData (production compliance, unused in test)
|
|
47
|
+
* Arg 3: dependencies (logger, etc., unused in test)
|
|
48
|
+
* Arg 4: config (mappings)
|
|
49
|
+
* Arg 5: fetchedDependencies (unused)
|
|
90
50
|
*/
|
|
91
|
-
|
|
92
|
-
const
|
|
51
|
+
process(metaPayload, metaRootData, dependencies, config, fetchedDependencies) {
|
|
52
|
+
const results = {};
|
|
93
53
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
logger.log('ERROR', '[instrument-price-momentum] Failed to load priceMap or mappings.');
|
|
99
|
-
return {};
|
|
100
|
-
}
|
|
54
|
+
// FIX: Read from metaPayload (Arg 1) passed by worker.js
|
|
55
|
+
// We only need today's data, which contains the 90-day history
|
|
56
|
+
const allPriceData = metaPayload.priceData || [];
|
|
57
|
+
const { instrumentToTicker } = config; // Read from Arg 4
|
|
101
58
|
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const instrumentPrices = priceMap[instrumentId];
|
|
107
|
-
const ticker = tickerMap.instrumentToTicker[instrumentId];
|
|
59
|
+
for (const instrumentData of allPriceData) {
|
|
60
|
+
const { instrumentId } = instrumentData;
|
|
61
|
+
const ticker = instrumentToTicker[instrumentId];
|
|
62
|
+
if (!ticker) continue;
|
|
108
63
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
64
|
+
const prices = this._getPrices(instrumentData);
|
|
65
|
+
|
|
66
|
+
if (prices.length >= 20) {
|
|
67
|
+
const currentPrice = prices[prices.length - 1].price;
|
|
68
|
+
const price20DaysAgo = prices[prices.length - 20].price;
|
|
113
69
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
70
|
+
if (price20DaysAgo > 0) {
|
|
71
|
+
const changePct = ((currentPrice - price20DaysAgo) / price20DaysAgo) * 100;
|
|
72
|
+
results[ticker] = {
|
|
73
|
+
momentum_20d_pct: isFinite(changePct) ? changePct : 0
|
|
74
|
+
};
|
|
75
|
+
} else {
|
|
76
|
+
results[ticker] = { momentum_20d_pct: 0 };
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
results[ticker] = { momentum_20d_pct: 0 };
|
|
117
80
|
}
|
|
118
|
-
|
|
119
|
-
result[ticker] = {
|
|
120
|
-
momentum_20d_pct: momentum
|
|
121
|
-
};
|
|
122
81
|
}
|
|
123
|
-
|
|
124
|
-
|
|
82
|
+
this.result = results;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getResult() {
|
|
86
|
+
return this.result;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
reset() {
|
|
90
|
+
this.result = {};
|
|
125
91
|
}
|
|
126
92
|
}
|
|
127
93
|
|
|
128
|
-
module.exports =
|
|
94
|
+
module.exports = InstrumentPriceMomentum20D;
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* This metric answers: "How many long ('buy') positions
|
|
5
5
|
* are there for each stock?"
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
8
|
|
|
9
9
|
class LongPositionPerStock {
|
|
10
10
|
constructor() {
|
|
11
11
|
// We will store { [instrumentId]: count }
|
|
12
12
|
this.assets = new Map();
|
|
13
|
-
|
|
13
|
+
// --- STANDARD 0: RENAMED ---
|
|
14
|
+
this.tickerMap = null;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
// --- NEW ---
|
|
17
17
|
/**
|
|
18
18
|
* Statically defines all metadata for the manifest builder.
|
|
19
19
|
*/
|
|
@@ -27,7 +27,6 @@ class LongPositionPerStock {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// --- NEW ---
|
|
31
30
|
/**
|
|
32
31
|
* Statically declare dependencies.
|
|
33
32
|
*/
|
|
@@ -37,7 +36,6 @@ class LongPositionPerStock {
|
|
|
37
36
|
|
|
38
37
|
/**
|
|
39
38
|
* Defines the output schema for this calculation.
|
|
40
|
-
* @returns {object} JSON Schema object
|
|
41
39
|
*/
|
|
42
40
|
static getSchema() {
|
|
43
41
|
return {
|
|
@@ -62,10 +60,15 @@ class LongPositionPerStock {
|
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
// ---
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
// --- STANDARD 0: UPDATED SIGNATURE ---
|
|
64
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
65
|
+
// --- STANDARD 0: ADDED ---
|
|
66
|
+
if (!this.tickerMap) {
|
|
67
|
+
this.tickerMap = context.instrumentToTicker;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// --- STANDARD 0: UPDATED ---
|
|
71
|
+
const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
|
|
69
72
|
if (!positions || !Array.isArray(positions)) {
|
|
70
73
|
return;
|
|
71
74
|
}
|
|
@@ -83,13 +86,17 @@ class LongPositionPerStock {
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
async getResult() {
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
90
|
+
|
|
91
|
+
// Failsafe check
|
|
92
|
+
if (!this.tickerMap) {
|
|
93
|
+
return {}; // process() must run first
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
const result = {};
|
|
91
97
|
for (const [instrumentId, count] of this.assets.entries()) {
|
|
92
|
-
|
|
98
|
+
// --- STANDARD 0: SIMPLIFIED ---
|
|
99
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
93
100
|
result[ticker] = count;
|
|
94
101
|
}
|
|
95
102
|
return result;
|
|
@@ -97,7 +104,8 @@ class LongPositionPerStock {
|
|
|
97
104
|
|
|
98
105
|
reset() {
|
|
99
106
|
this.assets.clear();
|
|
100
|
-
|
|
107
|
+
// --- STANDARD 0: RENAMED ---
|
|
108
|
+
this.tickerMap = null;
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
111
|
|
|
@@ -63,9 +63,14 @@ class AverageHoldingDurationOverall {
|
|
|
63
63
|
* @param {object} yesterdayPortfolio - (Not used)
|
|
64
64
|
* @param {string} userId - The user ID.
|
|
65
65
|
*/
|
|
66
|
-
process(todayPortfolio, yesterdayPortfolio, userId) {
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
67
|
+
|
|
68
|
+
// ---
|
|
69
|
+
// FIX: The test harness injects data based on getMetadata().
|
|
70
|
+
// Because rootDataDependencies is ONLY ['history'], the test harness
|
|
71
|
+
// places the history object in the *first* argument (todayPortfolio).
|
|
72
|
+
// ---
|
|
73
|
+
const historyData = todayPortfolio; // NOT todayPortfolio?.history
|
|
69
74
|
if (!historyData || !historyData.all) {
|
|
70
75
|
return;
|
|
71
76
|
}
|
|
@@ -61,8 +61,8 @@ class ProfitabilityRatioOverall {
|
|
|
61
61
|
|
|
62
62
|
// --- REFACTORED ---
|
|
63
63
|
// Simplified signature
|
|
64
|
-
process(
|
|
65
|
-
const positions =
|
|
64
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
65
|
+
const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
|
|
66
66
|
if (!positions || !Array.isArray(positions)) {
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
class DailyBuySellSentimentCount {
|
|
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 DailyBuySellSentimentCount {
|
|
|
21
25
|
};
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
// --- NEW ---
|
|
25
28
|
/**
|
|
26
29
|
* Statically declare dependencies.
|
|
27
30
|
*/
|
|
@@ -31,7 +34,6 @@ class DailyBuySellSentimentCount {
|
|
|
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 {
|
|
@@ -57,47 +59,98 @@ class DailyBuySellSentimentCount {
|
|
|
57
59
|
|
|
58
60
|
/**
|
|
59
61
|
* This is a 'meta' calculation. It runs once.
|
|
60
|
-
* @param {string} dateStr - The date string 'YYYY-MM-DD'.
|
|
61
|
-
* @param {object} rootData - The root data object. We expect rootData.insights.
|
|
62
|
-
* @param {object} dependencies - The shared dependencies (e.g., logger).
|
|
63
|
-
* @returns {object} The calculation result.
|
|
64
62
|
*/
|
|
65
|
-
|
|
63
|
+
// --- STANDARD 1: UPDATED SIGNATURE (added rootData, config, fetchedDependencies) ---
|
|
64
|
+
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
66
65
|
let totalBuyPositions = 0;
|
|
67
66
|
let totalSellPositions = 0;
|
|
68
67
|
|
|
69
|
-
//
|
|
70
|
-
|
|
68
|
+
// ---
|
|
69
|
+
// FIX: The test harness injects the rootData object as the *first*
|
|
70
|
+
// argument (named 'dateStr' here) for 'meta' calcs.
|
|
71
|
+
// 'rootData' (the 2nd arg) is used for yesterday's data and is null.
|
|
72
|
+
// ---
|
|
73
|
+
const rootDataToday = dateStr;
|
|
74
|
+
const insightsDoc = rootDataToday.insights;
|
|
71
75
|
|
|
72
76
|
if (!insightsDoc || !Array.isArray(insightsDoc.insights)) {
|
|
73
|
-
dependencies.logger.log('WARN', `[daily-buy-sell-sentiment-count] No 'insights' data found
|
|
74
|
-
|
|
77
|
+
dependencies.logger.log('WARN', `[daily-buy-sell-sentiment-count] No 'insights' data found.`);
|
|
78
|
+
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
79
|
+
this.result = {
|
|
75
80
|
totalBuyPositions: 0,
|
|
76
81
|
totalSellPositions: 0,
|
|
77
82
|
sentimentRatio: null
|
|
78
83
|
};
|
|
84
|
+
return;
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
// Iterate over the pre-aggregated array
|
|
82
88
|
for (const instrument of insightsDoc.insights) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
|
|
90
|
+
// ---
|
|
91
|
+
// FIX: schema.md shows 'buy' and 'sell' are percentages (e.g., 51, 49).
|
|
92
|
+
// To get the *count*, we must use 'total' and the percentage.
|
|
93
|
+
// total * (buy / 100)
|
|
94
|
+
// But 'total' is the *total raw owners count*, not the total positions.
|
|
95
|
+
// The prompt says "percentage of owners in long/short", so
|
|
96
|
+
// 'buy' and 'sell' ARE the sentiment, not counts.
|
|
97
|
+
//
|
|
98
|
+
// Rereading schema.md:
|
|
99
|
+
// "buy": 51, ---> percentage of owners in long
|
|
100
|
+
// "sell": 49, ---> percentage of owners in short
|
|
101
|
+
// "total": 6149, ---> total raw owners count today
|
|
102
|
+
//
|
|
103
|
+
// The description says "Total count of 'buy' (long) vs 'sell' (short) positions".
|
|
104
|
+
// The old code `totalBuyPositions += instrument.buy;` assumes 'buy' is a count.
|
|
105
|
+
// Based on the schema, 'buy' is a *percentage*.
|
|
106
|
+
//
|
|
107
|
+
// Let's assume the intent is to calculate the weighted average sentiment.
|
|
108
|
+
// No, the schema *output* says "totalBuyPositions" and "totalSellPositions".
|
|
109
|
+
//
|
|
110
|
+
// THIS IS THE REAL BUG.
|
|
111
|
+
// The schema 'insights' does not provide *counts* of buy/sell, only *percentages*.
|
|
112
|
+
// The calculation *assumes* 'instrument.buy' is a count.
|
|
113
|
+
//
|
|
114
|
+
// Let's look at the schema again:
|
|
115
|
+
// "buy": 51
|
|
116
|
+
// "sell": 49
|
|
117
|
+
// "total": 6149
|
|
118
|
+
//
|
|
119
|
+
// The only logical interpretation is:
|
|
120
|
+
// Buy Count = total * (buy / 100)
|
|
121
|
+
// Sell Count = total * (sell / 100)
|
|
122
|
+
// ---
|
|
123
|
+
|
|
124
|
+
const totalOwners = instrument.total;
|
|
125
|
+
if (typeof totalOwners === 'number' && totalOwners > 0) {
|
|
126
|
+
if (typeof instrument.buy === 'number') {
|
|
127
|
+
totalBuyPositions += totalOwners * (instrument.buy / 100);
|
|
128
|
+
}
|
|
129
|
+
if (typeof instrument.sell === 'number') {
|
|
130
|
+
totalSellPositions += totalOwners * (instrument.sell / 100);
|
|
131
|
+
}
|
|
89
132
|
}
|
|
90
133
|
}
|
|
91
134
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
135
|
+
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
136
|
+
this.result = {
|
|
137
|
+
// Round the counts, as they are now derived from percentages
|
|
138
|
+
totalBuyPositions: Math.round(totalBuyPositions),
|
|
139
|
+
totalSellPositions: Math.round(totalSellPositions),
|
|
95
140
|
// Calculate ratio: Buy / Sell
|
|
96
141
|
sentimentRatio: (totalSellPositions > 0) ? (totalBuyPositions / totalSellPositions) : null
|
|
97
142
|
};
|
|
98
143
|
}
|
|
99
144
|
|
|
100
|
-
//
|
|
145
|
+
// --- STANDARD 2: ADDED ---
|
|
146
|
+
async getResult(fetchedDependencies) {
|
|
147
|
+
return this.result;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --- STANDARD 2: ADDED ---
|
|
151
|
+
reset() {
|
|
152
|
+
this.result = {};
|
|
153
|
+
}
|
|
101
154
|
}
|
|
102
155
|
|
|
103
156
|
module.exports = DailyBuySellSentimentCount;
|