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,148 +1,92 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass
|
|
3
|
-
*
|
|
4
|
-
* This metric tracks the total number of positions 'bought' (new)
|
|
5
|
-
* and 'sold' (closed) today, based on daily ownership change.
|
|
6
|
-
*
|
|
7
|
-
* This is different from 'daily_asset_activity' because it counts
|
|
8
|
-
* *positions*, not *unique users*.
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for daily bought vs. sold count.
|
|
3
|
+
* REFACTORED: Uses context.math.extract on today vs yesterday.
|
|
9
4
|
*/
|
|
10
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
11
|
-
|
|
12
|
-
|
|
13
5
|
class DailyBoughtVsSoldCount {
|
|
14
6
|
constructor() {
|
|
15
|
-
// We will store { [instrumentId]: { new: 0, closed: 0 } }
|
|
16
7
|
this.assetActivity = new Map();
|
|
17
|
-
// --- STANDARD 0: RENAMED ---
|
|
18
8
|
this.tickerMap = null;
|
|
19
9
|
}
|
|
20
10
|
|
|
21
|
-
/**
|
|
22
|
-
* Statically defines all metadata for the manifest builder.
|
|
23
|
-
*/
|
|
24
11
|
static getMetadata() {
|
|
25
12
|
return {
|
|
26
13
|
type: 'standard',
|
|
27
14
|
rootDataDependencies: ['portfolio'],
|
|
28
|
-
isHistorical: true,
|
|
15
|
+
isHistorical: true,
|
|
29
16
|
userType: 'all',
|
|
30
17
|
category: 'core_metrics'
|
|
31
18
|
};
|
|
32
19
|
}
|
|
33
20
|
|
|
34
|
-
|
|
35
|
-
* Statically declare dependencies.
|
|
36
|
-
*/
|
|
37
|
-
static getDependencies() {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
40
22
|
|
|
41
|
-
/**
|
|
42
|
-
* Defines the output schema for this calculation.
|
|
43
|
-
*/
|
|
44
23
|
static getSchema() {
|
|
45
24
|
const tickerSchema = {
|
|
46
25
|
"type": "object",
|
|
47
|
-
"description": "Daily trade activity for a specific asset.",
|
|
48
26
|
"properties": {
|
|
49
|
-
"positions_bought": {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
"positions_sold": {
|
|
54
|
-
"type": "number",
|
|
55
|
-
"description": "Total positions closed for this asset."
|
|
56
|
-
},
|
|
57
|
-
"net_change": {
|
|
58
|
-
"type": "number",
|
|
59
|
-
"description": "Net change in positions (bought - sold)."
|
|
60
|
-
}
|
|
27
|
+
"positions_bought": { "type": "number" },
|
|
28
|
+
"positions_sold": { "type": "number" },
|
|
29
|
+
"net_change": { "type": "number" }
|
|
61
30
|
},
|
|
62
31
|
"required": ["positions_bought", "positions_sold", "net_change"]
|
|
63
32
|
};
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
"type": "object",
|
|
67
|
-
"description": "Tracks new positions (bought) and closed positions (sold) per asset.",
|
|
68
|
-
"patternProperties": {
|
|
69
|
-
"^.*$": tickerSchema // Matches any string key (ticker)
|
|
70
|
-
},
|
|
71
|
-
"additionalProperties": tickerSchema
|
|
72
|
-
};
|
|
33
|
+
return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
|
|
73
34
|
}
|
|
74
35
|
|
|
75
36
|
_initAsset(instrumentId) {
|
|
76
37
|
if (!this.assetActivity.has(instrumentId)) {
|
|
77
|
-
this.assetActivity.set(instrumentId, {
|
|
78
|
-
new: 0,
|
|
79
|
-
closed: 0
|
|
80
|
-
});
|
|
38
|
+
this.assetActivity.set(instrumentId, { new: 0, closed: 0 });
|
|
81
39
|
}
|
|
82
40
|
}
|
|
83
41
|
|
|
84
|
-
_getInstrumentIds(
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
42
|
+
_getInstrumentIds(positions, extract) {
|
|
43
|
+
const map = new Map();
|
|
44
|
+
for (const pos of positions) {
|
|
45
|
+
const posId = extract.getPositionId(pos);
|
|
46
|
+
const instId = extract.getInstrumentId(pos);
|
|
47
|
+
if (posId && instId) map.set(posId, instId);
|
|
89
48
|
}
|
|
90
|
-
|
|
91
|
-
return new Map(positions.map(p => [p.PositionID, p.InstrumentID]).filter(p => p[0] && p[1]));
|
|
49
|
+
return map;
|
|
92
50
|
}
|
|
93
51
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!this.tickerMap)
|
|
98
|
-
this.tickerMap = context.instrumentToTicker;
|
|
99
|
-
}
|
|
52
|
+
process(context) {
|
|
53
|
+
const { extract } = context.math;
|
|
54
|
+
const { mappings, user } = context;
|
|
55
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
100
56
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
57
|
+
const todayPositions = extract.getPositions(user.portfolio.today, user.type);
|
|
58
|
+
const yesterdayPositions = extract.getPositions(user.portfolio.yesterday, user.type);
|
|
104
59
|
|
|
105
|
-
const
|
|
106
|
-
const
|
|
60
|
+
const tPosMap = this._getInstrumentIds(todayPositions, extract);
|
|
61
|
+
const yPosMap = this._getInstrumentIds(yesterdayPositions, extract);
|
|
107
62
|
|
|
108
|
-
//
|
|
109
|
-
for (const [
|
|
110
|
-
if (!yPosMap.has(
|
|
111
|
-
this._initAsset(
|
|
112
|
-
this.assetActivity.get(
|
|
63
|
+
// New positions (in today, not yesterday)
|
|
64
|
+
for (const [posId, instId] of tPosMap.entries()) {
|
|
65
|
+
if (!yPosMap.has(posId)) {
|
|
66
|
+
this._initAsset(instId);
|
|
67
|
+
this.assetActivity.get(instId).new++;
|
|
113
68
|
}
|
|
114
69
|
}
|
|
115
70
|
|
|
116
|
-
//
|
|
117
|
-
for (const [
|
|
118
|
-
if (!tPosMap.has(
|
|
119
|
-
this._initAsset(
|
|
120
|
-
this.assetActivity.get(
|
|
71
|
+
// Closed positions (in yesterday, not today)
|
|
72
|
+
for (const [posId, instId] of yPosMap.entries()) {
|
|
73
|
+
if (!tPosMap.has(posId)) {
|
|
74
|
+
this._initAsset(instId);
|
|
75
|
+
this.assetActivity.get(instId).closed++;
|
|
121
76
|
}
|
|
122
77
|
}
|
|
123
78
|
}
|
|
124
79
|
|
|
125
80
|
async getResult() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// Failsafe check
|
|
129
|
-
if (!this.tickerMap) {
|
|
130
|
-
return {}; // process() must run first
|
|
131
|
-
}
|
|
132
|
-
|
|
81
|
+
if (!this.tickerMap) return {};
|
|
133
82
|
const result = {};
|
|
134
|
-
for (const [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const openCount = data.new;
|
|
139
|
-
const closeCount = data.closed;
|
|
140
|
-
|
|
141
|
-
if (openCount > 0 || closeCount > 0) {
|
|
83
|
+
for (const [instId, data] of this.assetActivity.entries()) {
|
|
84
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
85
|
+
if (data.new > 0 || data.closed > 0) {
|
|
142
86
|
result[ticker] = {
|
|
143
|
-
positions_bought:
|
|
144
|
-
positions_sold:
|
|
145
|
-
net_change:
|
|
87
|
+
positions_bought: data.new,
|
|
88
|
+
positions_sold: data.closed,
|
|
89
|
+
net_change: data.new - data.closed
|
|
146
90
|
};
|
|
147
91
|
}
|
|
148
92
|
}
|
|
@@ -151,9 +95,7 @@ class DailyBoughtVsSoldCount {
|
|
|
151
95
|
|
|
152
96
|
reset() {
|
|
153
97
|
this.assetActivity.clear();
|
|
154
|
-
// --- STANDARD 0: RENAMED ---
|
|
155
98
|
this.tickerMap = null;
|
|
156
99
|
}
|
|
157
100
|
}
|
|
158
|
-
|
|
159
101
|
module.exports = DailyBoughtVsSoldCount;
|
|
@@ -1,161 +1,103 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for
|
|
3
|
-
*
|
|
4
|
-
* This metric calculates the daily change in the total number of *owners*
|
|
5
|
-
* (unique users) for each instrument.
|
|
6
|
-
*
|
|
7
|
-
* REFACTOR: This is now a 'type: "meta"' and 'isHistorical: true' calculation.
|
|
8
|
-
* It runs ONCE, loads today's and yesterday's pre-aggregated 'insights' docs,
|
|
9
|
-
* and calculates the delta based on the 'total' field.
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for ownership delta (users added/lost per asset).
|
|
3
|
+
* REFACTORED: Tracks net change in number of owners. ---> TODO IN TEST ENVIRONMENT THE VALUE OF NUMBER OF CLOSURES ALWAYS RETURNS 0, IS THIS A COMPUTATION BUG OR A TEST HARNESS BUG?
|
|
10
4
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class DailyOwnershipDelta {
|
|
15
|
-
|
|
16
|
-
// --- STANDARD 2: ADDED ---
|
|
5
|
+
class PlatformDailyOwnershipDelta {
|
|
17
6
|
constructor() {
|
|
18
|
-
this.
|
|
7
|
+
this.assetChanges = new Map(); // { instId: { added: 0, removed: 0 } }
|
|
8
|
+
this.tickerMap = null;
|
|
19
9
|
}
|
|
20
10
|
|
|
21
|
-
/**
|
|
22
|
-
* Statically defines all metadata for the manifest builder.
|
|
23
|
-
*/
|
|
24
11
|
static getMetadata() {
|
|
25
12
|
return {
|
|
26
|
-
type: '
|
|
27
|
-
rootDataDependencies: ['
|
|
28
|
-
isHistorical: true,
|
|
29
|
-
userType: '
|
|
13
|
+
type: 'standard',
|
|
14
|
+
rootDataDependencies: ['portfolio'],
|
|
15
|
+
isHistorical: true,
|
|
16
|
+
userType: 'all',
|
|
30
17
|
category: 'core_metrics'
|
|
31
18
|
};
|
|
32
19
|
}
|
|
33
20
|
|
|
34
|
-
|
|
35
|
-
* Statically declare dependencies.
|
|
36
|
-
*/
|
|
37
|
-
static getDependencies() {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
40
22
|
|
|
41
|
-
/**
|
|
42
|
-
* Defines the output schema for this calculation.
|
|
43
|
-
*/
|
|
44
23
|
static getSchema() {
|
|
45
24
|
const tickerSchema = {
|
|
46
25
|
"type": "object",
|
|
47
|
-
"description": "Daily change in unique owners for a specific asset.",
|
|
48
26
|
"properties": {
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
"owners_yesterday": {
|
|
54
|
-
"type": "number",
|
|
55
|
-
"description": "Total unique users holding this asset yesterday."
|
|
56
|
-
},
|
|
57
|
-
"owner_delta": {
|
|
58
|
-
"type": "number",
|
|
59
|
-
"description": "The net change in unique owners (today - yesterday)."
|
|
60
|
-
},
|
|
61
|
-
"owner_delta_percent": {
|
|
62
|
-
"type": ["number", "null"],
|
|
63
|
-
"description": "Percentage change in unique owners. Null if yesterday had 0 owners."
|
|
64
|
-
}
|
|
27
|
+
"owners_added": { "type": "number" },
|
|
28
|
+
"owners_removed": { "type": "number" },
|
|
29
|
+
"net_ownership_change": { "type": "number" }
|
|
65
30
|
},
|
|
66
|
-
"required": ["
|
|
31
|
+
"required": ["owners_added", "owners_removed", "net_ownership_change"]
|
|
67
32
|
};
|
|
33
|
+
return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
|
|
34
|
+
}
|
|
68
35
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
36
|
+
_initAsset(instId) {
|
|
37
|
+
if (!this.assetChanges.has(instId)) {
|
|
38
|
+
this.assetChanges.set(instId, { added: 0, removed: 0 });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_getOwnedInstruments(positions, extract) {
|
|
43
|
+
const set = new Set();
|
|
44
|
+
for (const pos of positions) {
|
|
45
|
+
const id = extract.getInstrumentId(pos);
|
|
46
|
+
if (id) set.add(id);
|
|
47
|
+
}
|
|
48
|
+
return set;
|
|
77
49
|
}
|
|
78
50
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
107
|
-
// 1. Process today's insights doc
|
|
108
|
-
if (todayDoc && Array.isArray(todayDoc.insights)) {
|
|
109
|
-
for (const instrument of todayDoc.insights) {
|
|
110
|
-
const id = instrument.instrumentId;
|
|
111
|
-
const totalOwners = instrument.total || 0; // 'total' is the owner count
|
|
112
|
-
todayOwners.set(id, totalOwners);
|
|
113
|
-
allInstrumentIds.add(id);
|
|
51
|
+
process(context) {
|
|
52
|
+
const { extract } = context.math;
|
|
53
|
+
const { mappings, user } = context;
|
|
54
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
55
|
+
|
|
56
|
+
// If either portfolio is missing, we can't calculate delta for this user
|
|
57
|
+
if (!user.portfolio.today || !user.portfolio.yesterday) return;
|
|
58
|
+
|
|
59
|
+
const todayPositions = extract.getPositions(user.portfolio.today, user.type);
|
|
60
|
+
const yesterdayPositions = extract.getPositions(user.portfolio.yesterday, user.type);
|
|
61
|
+
|
|
62
|
+
const tSet = this._getOwnedInstruments(todayPositions, extract);
|
|
63
|
+
const ySet = this._getOwnedInstruments(yesterdayPositions, extract);
|
|
64
|
+
|
|
65
|
+
// Added: In Today, Not Yesterday
|
|
66
|
+
for (const instId of tSet) {
|
|
67
|
+
if (!ySet.has(instId)) {
|
|
68
|
+
this._initAsset(instId);
|
|
69
|
+
this.assetChanges.get(instId).added++;
|
|
114
70
|
}
|
|
115
71
|
}
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
yesterdayOwners.set(id, totalOwners);
|
|
123
|
-
allInstrumentIds.add(id);
|
|
72
|
+
|
|
73
|
+
// Removed: In Yesterday, Not Today
|
|
74
|
+
for (const instId of ySet) {
|
|
75
|
+
if (!tSet.has(instId)) {
|
|
76
|
+
this._initAsset(instId);
|
|
77
|
+
this.assetChanges.get(instId).removed++;
|
|
124
78
|
}
|
|
125
79
|
}
|
|
126
|
-
|
|
127
|
-
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getResult() {
|
|
83
|
+
if (!this.tickerMap) return {};
|
|
128
84
|
const result = {};
|
|
129
|
-
for (const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const tOwners = todayOwners.get(instrumentId) || 0;
|
|
134
|
-
const yOwners = yesterdayOwners.get(instrumentId) || 0;
|
|
135
|
-
|
|
136
|
-
if (yOwners > 0 || tOwners > 0) {
|
|
85
|
+
for (const [instId, data] of this.assetChanges.entries()) {
|
|
86
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
87
|
+
if (data.added > 0 || data.removed > 0) {
|
|
137
88
|
result[ticker] = {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
owner_delta_percent: (yOwners > 0) ? ((tOwners - yOwners) / yOwners) * 100 : null
|
|
89
|
+
owners_added: data.added,
|
|
90
|
+
owners_removed: data.removed,
|
|
91
|
+
net_ownership_change: data.added - data.removed
|
|
142
92
|
};
|
|
143
93
|
}
|
|
144
94
|
}
|
|
145
|
-
|
|
146
|
-
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
147
|
-
this.result = result;
|
|
95
|
+
return result;
|
|
148
96
|
}
|
|
149
97
|
|
|
150
|
-
// --- STANDARD 2: ADDED ---
|
|
151
|
-
async getResult(fetchedDependencies) {
|
|
152
|
-
return this.result;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// --- STANDARD 2: ADDED ---
|
|
156
98
|
reset() {
|
|
157
|
-
this.
|
|
99
|
+
this.assetChanges.clear();
|
|
100
|
+
this.tickerMap = null;
|
|
158
101
|
}
|
|
159
102
|
}
|
|
160
|
-
|
|
161
|
-
module.exports = DailyOwnershipDelta;
|
|
103
|
+
module.exports = PlatformDailyOwnershipDelta;
|
|
@@ -1,129 +1,78 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for
|
|
3
|
-
*
|
|
4
|
-
* This is a 'type: "meta"' calculation. It runs ONCE.
|
|
5
|
-
* It reads the pre-aggregated 'insights' data source (/daily_instrument_insights/)
|
|
6
|
-
* and uses the sector mapping provider to aggregate the total number
|
|
7
|
-
* of owners for each sector.
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for sector ownership.
|
|
3
|
+
* REFACTORED: Uses exposure weight.
|
|
8
4
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class DailyOwnershipPerSector {
|
|
12
|
-
|
|
13
|
-
// --- STANDARD 2: ADDED ---
|
|
5
|
+
class PlatformOwnershipPerSector {
|
|
14
6
|
constructor() {
|
|
15
|
-
this.
|
|
7
|
+
this.sectorData = new Map();
|
|
8
|
+
this.sectorMap = null;
|
|
16
9
|
}
|
|
17
10
|
|
|
18
|
-
/**
|
|
19
|
-
* Statically defines all metadata for the manifest builder.
|
|
20
|
-
*/
|
|
21
11
|
static getMetadata() {
|
|
22
12
|
return {
|
|
23
|
-
type: '
|
|
24
|
-
rootDataDependencies: ['
|
|
13
|
+
type: 'standard',
|
|
14
|
+
rootDataDependencies: ['portfolio'],
|
|
25
15
|
isHistorical: false,
|
|
26
|
-
userType: '
|
|
27
|
-
category: '
|
|
16
|
+
userType: 'all',
|
|
17
|
+
category: 'core'
|
|
28
18
|
};
|
|
29
19
|
}
|
|
30
20
|
|
|
31
|
-
|
|
32
|
-
* Statically declare dependencies.
|
|
33
|
-
*/
|
|
34
|
-
static getDependencies() {
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
37
22
|
|
|
38
|
-
/**
|
|
39
|
-
* Defines the output schema for this calculation.
|
|
40
|
-
*/
|
|
41
23
|
static getSchema() {
|
|
42
|
-
const
|
|
24
|
+
const schema = {
|
|
43
25
|
"type": "object",
|
|
44
|
-
"description": "Aggregated ownership for a single sector.",
|
|
45
26
|
"properties": {
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
"description": "The total number of unique owners for all assets in this sector."
|
|
49
|
-
}
|
|
27
|
+
"total_exposure_weight": { "type": "number" },
|
|
28
|
+
"position_count": { "type": "number" }
|
|
50
29
|
},
|
|
51
|
-
"required": ["
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
"type": "object",
|
|
56
|
-
"description": "Calculates the total unique owners per sector based on the 'insights' data source.",
|
|
57
|
-
"patternProperties": {
|
|
58
|
-
"^.*$": sectorSchema // Matches any string key (sector name)
|
|
59
|
-
},
|
|
60
|
-
"additionalProperties": sectorSchema
|
|
30
|
+
"required": ["total_exposure_weight", "position_count"]
|
|
61
31
|
};
|
|
32
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
62
33
|
}
|
|
63
34
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// --- STANDARD 1: UPDATED SIGNATURE ---
|
|
68
|
-
async process(dateStr, rootData, dependencies, config, fetchedDependencies) {
|
|
69
|
-
// { [sectorName]: { total_owners: 0 } }
|
|
70
|
-
const sectorOwners = new Map();
|
|
71
|
-
|
|
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
|
|
80
|
-
|
|
81
|
-
// 2. Get the insights document
|
|
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
|
-
|
|
87
|
-
if (!insightsDoc || !Array.isArray(insightsDoc.insights)) {
|
|
88
|
-
logger.log('WARN', `[daily-ownership-per-sector] No 'insights' data found.`);
|
|
89
|
-
this.result = {};
|
|
90
|
-
return;
|
|
35
|
+
_initSector(sector) {
|
|
36
|
+
if (!this.sectorData.has(sector)) {
|
|
37
|
+
this.sectorData.set(sector, { weight: 0, count: 0 });
|
|
91
38
|
}
|
|
39
|
+
}
|
|
92
40
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
41
|
+
process(context) {
|
|
42
|
+
const { extract } = context.math;
|
|
43
|
+
const { mappings, user } = context;
|
|
44
|
+
if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
|
|
97
45
|
|
|
98
|
-
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
46
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
101
47
|
|
|
102
|
-
|
|
103
|
-
const
|
|
48
|
+
for (const pos of positions) {
|
|
49
|
+
const instId = extract.getInstrumentId(pos);
|
|
50
|
+
if (!instId) continue;
|
|
104
51
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
sectorOwners.set(sectorName, { total_owners: 0 });
|
|
108
|
-
}
|
|
52
|
+
const sector = this.sectorMap[instId] || 'Unknown';
|
|
53
|
+
const weight = extract.getPositionWeight(pos, user.type);
|
|
109
54
|
|
|
110
|
-
|
|
111
|
-
|
|
55
|
+
this._initSector(sector);
|
|
56
|
+
const data = this.sectorData.get(sector);
|
|
57
|
+
data.weight += weight;
|
|
58
|
+
data.count++;
|
|
112
59
|
}
|
|
113
|
-
|
|
114
|
-
// --- STANDARD 2: SET STATE, DO NOT RETURN ---
|
|
115
|
-
this.result = Object.fromEntries(sectorOwners);
|
|
116
60
|
}
|
|
117
61
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
62
|
+
async getResult() {
|
|
63
|
+
const result = {};
|
|
64
|
+
for (const [sector, data] of this.sectorData.entries()) {
|
|
65
|
+
result[sector] = {
|
|
66
|
+
total_exposure_weight: data.weight,
|
|
67
|
+
position_count: data.count
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
121
71
|
}
|
|
122
72
|
|
|
123
|
-
// --- STANDARD 2: ADDED ---
|
|
124
73
|
reset() {
|
|
125
|
-
this.
|
|
74
|
+
this.sectorData.clear();
|
|
75
|
+
this.sectorMap = null;
|
|
126
76
|
}
|
|
127
77
|
}
|
|
128
|
-
|
|
129
|
-
module.exports = DailyOwnershipPerSector;
|
|
78
|
+
module.exports = PlatformOwnershipPerSector;
|