aiden-shared-calculations-unified 1.0.86 → 1.0.87
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 +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/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/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/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,42 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Calculation (Pass 1) for speculator TSL usage.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "For each asset, what percentage
|
|
5
|
-
* of speculators are using a *trailing* stop-loss?"
|
|
3
|
+
* REFACTORED: Uses extraction for TSL boolean.
|
|
6
4
|
*/
|
|
7
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
|
-
|
|
9
5
|
class SpeculatorTSLPerAsset {
|
|
10
6
|
constructor() {
|
|
11
|
-
// { [instrumentId]: { with_tsl: 0, total: 0 } }
|
|
12
7
|
this.assets = new Map();
|
|
13
|
-
// --- STANDARD 0: RENAMED ---
|
|
14
8
|
this.tickerMap = null;
|
|
15
9
|
}
|
|
16
10
|
|
|
17
|
-
/**
|
|
18
|
-
* Statically defines all metadata for the manifest builder.
|
|
19
|
-
*/
|
|
20
11
|
static getMetadata() {
|
|
21
12
|
return {
|
|
22
13
|
type: 'standard',
|
|
23
14
|
rootDataDependencies: ['portfolio'],
|
|
24
15
|
isHistorical: false,
|
|
25
|
-
userType: 'speculator',
|
|
16
|
+
userType: 'speculator',
|
|
26
17
|
category: 'core_speculator'
|
|
27
18
|
};
|
|
28
19
|
}
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
* Statically declare dependencies.
|
|
32
|
-
*/
|
|
33
|
-
static getDependencies() {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
36
22
|
|
|
37
|
-
/**
|
|
38
|
-
* Defines the output schema for this calculation.
|
|
39
|
-
*/
|
|
40
23
|
static getSchema() {
|
|
41
24
|
const tickerSchema = {
|
|
42
25
|
"type": "object",
|
|
@@ -47,13 +30,7 @@ class SpeculatorTSLPerAsset {
|
|
|
47
30
|
},
|
|
48
31
|
"required": ["tsl_usage_pct", "tsl_count", "total_count"]
|
|
49
32
|
};
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
"type": "object",
|
|
53
|
-
"description": "Calculates the percentage of speculators using a *trailing* stop-loss per asset.",
|
|
54
|
-
"patternProperties": { "^.*$": tickerSchema },
|
|
55
|
-
"additionalProperties": tickerSchema
|
|
56
|
-
};
|
|
33
|
+
return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
|
|
57
34
|
}
|
|
58
35
|
|
|
59
36
|
_initAsset(instrumentId) {
|
|
@@ -62,46 +39,32 @@ class SpeculatorTSLPerAsset {
|
|
|
62
39
|
}
|
|
63
40
|
}
|
|
64
41
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!this.tickerMap)
|
|
69
|
-
this.tickerMap = context.instrumentToTicker;
|
|
70
|
-
}
|
|
42
|
+
process(context) {
|
|
43
|
+
const { extract } = context.math;
|
|
44
|
+
const { mappings, user } = context;
|
|
45
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
71
46
|
|
|
72
|
-
const positions =
|
|
73
|
-
if (!positions || !Array.isArray(positions)) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
47
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
76
48
|
|
|
77
49
|
for (const pos of positions) {
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
50
|
+
const instId = extract.getInstrumentId(pos);
|
|
51
|
+
if (!instId) continue;
|
|
80
52
|
|
|
81
|
-
this._initAsset(
|
|
82
|
-
const assetData = this.assets.get(
|
|
53
|
+
this._initAsset(instId);
|
|
54
|
+
const assetData = this.assets.get(instId);
|
|
83
55
|
assetData.total++;
|
|
84
56
|
|
|
85
|
-
|
|
86
|
-
if (pos.HasTrailingStopLoss === true) {
|
|
57
|
+
if (extract.getHasTSL(pos)) {
|
|
87
58
|
assetData.with_tsl++;
|
|
88
59
|
}
|
|
89
60
|
}
|
|
90
61
|
}
|
|
91
62
|
|
|
92
63
|
async getResult() {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Failsafe check
|
|
96
|
-
if (!this.tickerMap) {
|
|
97
|
-
return {}; // process() must run first
|
|
98
|
-
}
|
|
99
|
-
|
|
64
|
+
if (!this.tickerMap) return {};
|
|
100
65
|
const result = {};
|
|
101
|
-
for (const [
|
|
102
|
-
|
|
103
|
-
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
104
|
-
|
|
66
|
+
for (const [instId, data] of this.assets.entries()) {
|
|
67
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
105
68
|
if (data.total > 0) {
|
|
106
69
|
result[ticker] = {
|
|
107
70
|
tsl_usage_pct: (data.with_tsl / data.total) * 100,
|
|
@@ -115,9 +78,7 @@ class SpeculatorTSLPerAsset {
|
|
|
115
78
|
|
|
116
79
|
reset() {
|
|
117
80
|
this.assets.clear();
|
|
118
|
-
// --- STANDARD 0: RENAMED ---
|
|
119
81
|
this.tickerMap = null;
|
|
120
82
|
}
|
|
121
83
|
}
|
|
122
|
-
|
|
123
84
|
module.exports = SpeculatorTSLPerAsset;
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Calculation (Pass 1) for total long figures.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "What is the total $USD value
|
|
5
|
-
* and total count of all long ('buy') positions?"
|
|
3
|
+
* REFACTORED: Tracks exposure weight (percentage of equity), not USD.
|
|
6
4
|
*/
|
|
7
5
|
class TotalLongFigures {
|
|
8
6
|
constructor() {
|
|
9
|
-
this.
|
|
7
|
+
this.totalExposureWeight = 0;
|
|
10
8
|
this.totalPositions = 0;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
/**
|
|
14
|
-
* Statically defines all metadata for the manifest builder.
|
|
15
|
-
*/
|
|
16
11
|
static getMetadata() {
|
|
17
12
|
return {
|
|
18
13
|
type: 'standard',
|
|
@@ -23,40 +18,30 @@ class TotalLongFigures {
|
|
|
23
18
|
};
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
* Statically declare dependencies.
|
|
28
|
-
*/
|
|
29
|
-
static getDependencies() {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
32
22
|
|
|
33
|
-
/**
|
|
34
|
-
* Defines the output schema for this calculation.
|
|
35
|
-
*/
|
|
36
23
|
static getSchema() {
|
|
37
24
|
return {
|
|
38
25
|
"type": "object",
|
|
39
|
-
"description": "Aggregates the total $USD value and count of all long positions.",
|
|
40
26
|
"properties": {
|
|
41
|
-
"
|
|
27
|
+
"total_long_exposure_weight": { "type": "number", "description": "Sum of portfolio allocation percentages for all long positions." },
|
|
42
28
|
"total_positions_count": { "type": "number" }
|
|
43
29
|
},
|
|
44
|
-
"required": ["
|
|
30
|
+
"required": ["total_long_exposure_weight", "total_positions_count"]
|
|
45
31
|
};
|
|
46
32
|
}
|
|
47
33
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
34
|
+
process(context) {
|
|
35
|
+
const { extract } = context.math;
|
|
36
|
+
const { user } = context;
|
|
37
|
+
|
|
38
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
55
39
|
|
|
56
40
|
for (const pos of positions) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
41
|
+
const direction = extract.getDirection(pos);
|
|
42
|
+
|
|
43
|
+
if (direction === 'Buy') {
|
|
44
|
+
this.totalExposureWeight += extract.getPositionWeight(pos, user.type);
|
|
60
45
|
this.totalPositions++;
|
|
61
46
|
}
|
|
62
47
|
}
|
|
@@ -64,13 +49,13 @@ class TotalLongFigures {
|
|
|
64
49
|
|
|
65
50
|
getResult() {
|
|
66
51
|
return {
|
|
67
|
-
|
|
52
|
+
total_long_exposure_weight: this.totalExposureWeight,
|
|
68
53
|
total_positions_count: this.totalPositions
|
|
69
54
|
};
|
|
70
55
|
}
|
|
71
56
|
|
|
72
57
|
reset() {
|
|
73
|
-
this.
|
|
58
|
+
this.totalExposureWeight = 0;
|
|
74
59
|
this.totalPositions = 0;
|
|
75
60
|
}
|
|
76
61
|
}
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "What is the total $USD value
|
|
5
|
-
* of all long ('buy') positions, grouped by sector?"
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for long figures per sector.
|
|
3
|
+
* REFACTORED: Tracks exposure weight (percentage).
|
|
6
4
|
*/
|
|
7
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
|
-
|
|
9
5
|
class TotalLongPerSector {
|
|
10
6
|
constructor() {
|
|
11
|
-
|
|
12
|
-
this.sectors = new Map();
|
|
13
|
-
// --- STANDARD 0: RENAMED ---
|
|
7
|
+
this.sectorData = new Map();
|
|
14
8
|
this.sectorMap = null;
|
|
15
9
|
}
|
|
16
10
|
|
|
17
|
-
/**
|
|
18
|
-
* Statically defines all metadata for the manifest builder.
|
|
19
|
-
*/
|
|
20
11
|
static getMetadata() {
|
|
21
12
|
return {
|
|
22
13
|
type: 'standard',
|
|
@@ -27,78 +18,65 @@ class TotalLongPerSector {
|
|
|
27
18
|
};
|
|
28
19
|
}
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
* Statically declare dependencies.
|
|
32
|
-
*/
|
|
33
|
-
static getDependencies() {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
36
22
|
|
|
37
|
-
/**
|
|
38
|
-
* Defines the output schema for this calculation.
|
|
39
|
-
*/
|
|
40
23
|
static getSchema() {
|
|
41
|
-
const
|
|
24
|
+
const schema = {
|
|
42
25
|
"type": "object",
|
|
43
26
|
"properties": {
|
|
44
|
-
"
|
|
27
|
+
"total_long_exposure_weight": { "type": "number" },
|
|
45
28
|
"total_positions_count": { "type": "number" }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
"type": "object",
|
|
51
|
-
"description": "Calculates the total $USD value and count of long positions per sector.",
|
|
52
|
-
"patternProperties": { "^.*$": sectorSchema },
|
|
53
|
-
"additionalProperties": sectorSchema
|
|
29
|
+
},
|
|
30
|
+
"required": ["total_long_exposure_weight", "total_positions_count"]
|
|
54
31
|
};
|
|
32
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
55
33
|
}
|
|
56
34
|
|
|
57
|
-
_initSector(
|
|
58
|
-
if (!this.
|
|
59
|
-
this.
|
|
35
|
+
_initSector(sector) {
|
|
36
|
+
if (!this.sectorData.has(sector)) {
|
|
37
|
+
this.sectorData.set(sector, { weight: 0, count: 0 });
|
|
60
38
|
}
|
|
61
39
|
}
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!this.sectorMap)
|
|
67
|
-
this.sectorMap = context.sectorMapping;
|
|
68
|
-
}
|
|
41
|
+
process(context) {
|
|
42
|
+
const { extract } = context.math;
|
|
43
|
+
const { mappings, user } = context;
|
|
44
|
+
if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
|
|
69
45
|
|
|
70
|
-
const positions =
|
|
71
|
-
if (!positions || !Array.isArray(positions) || !this.sectorMap) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
46
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
74
47
|
|
|
75
48
|
for (const pos of positions) {
|
|
76
|
-
|
|
77
|
-
if (pos.IsBuy) {
|
|
78
|
-
const instrumentId = pos.InstrumentID;
|
|
79
|
-
if (!instrumentId) continue;
|
|
80
|
-
|
|
81
|
-
// --- STANDARD 0: FIXED ---
|
|
82
|
-
const sectorName = this.sectorMap[instrumentId] || 'N/A';
|
|
83
|
-
this._initSector(sectorName);
|
|
84
|
-
const sectorData = this.sectors.get(sectorName);
|
|
49
|
+
if (extract.getDirection(pos) !== 'Buy') continue;
|
|
85
50
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
51
|
+
const instId = extract.getInstrumentId(pos);
|
|
52
|
+
if (!instId) continue;
|
|
53
|
+
|
|
54
|
+
const sector = this.sectorMap[instId] || 'Unknown';
|
|
55
|
+
const weight = extract.getPositionWeight(pos, user.type);
|
|
56
|
+
|
|
57
|
+
this._initSector(sector);
|
|
58
|
+
const data = this.sectorData.get(sector);
|
|
59
|
+
data.weight += weight;
|
|
60
|
+
data.count++;
|
|
89
61
|
}
|
|
90
62
|
}
|
|
91
63
|
|
|
92
64
|
async getResult() {
|
|
93
|
-
|
|
94
|
-
|
|
65
|
+
const result = {};
|
|
66
|
+
for (const [sector, data] of this.sectorData.entries()) {
|
|
67
|
+
if (data.count > 0) {
|
|
68
|
+
result[sector] = {
|
|
69
|
+
total_long_exposure_weight: data.weight,
|
|
70
|
+
total_positions_count: data.count
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
95
75
|
}
|
|
96
76
|
|
|
97
77
|
reset() {
|
|
98
|
-
this.
|
|
99
|
-
// --- STANDARD 0: RENAMED ---
|
|
78
|
+
this.sectorData.clear();
|
|
100
79
|
this.sectorMap = null;
|
|
101
80
|
}
|
|
102
81
|
}
|
|
103
|
-
|
|
104
82
|
module.exports = TotalLongPerSector;
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Calculation (Pass 1) for total short figures.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "What is the total $USD value
|
|
5
|
-
* and total count of all short ('sell') positions?"
|
|
3
|
+
* REFACTORED: Tracks exposure weight (percentage).
|
|
6
4
|
*/
|
|
7
5
|
class TotalShortFigures {
|
|
8
6
|
constructor() {
|
|
9
|
-
this.
|
|
7
|
+
this.totalExposureWeight = 0;
|
|
10
8
|
this.totalPositions = 0;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
/**
|
|
14
|
-
* Statically defines all metadata for the manifest builder.
|
|
15
|
-
*/
|
|
16
11
|
static getMetadata() {
|
|
17
12
|
return {
|
|
18
13
|
type: 'standard',
|
|
@@ -23,40 +18,27 @@ class TotalShortFigures {
|
|
|
23
18
|
};
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
* Statically declare dependencies.
|
|
28
|
-
*/
|
|
29
|
-
static getDependencies() {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
32
22
|
|
|
33
|
-
/**
|
|
34
|
-
* Defines the output schema for this calculation.
|
|
35
|
-
*/
|
|
36
23
|
static getSchema() {
|
|
37
24
|
return {
|
|
38
25
|
"type": "object",
|
|
39
|
-
"description": "Aggregates the total $USD value and count of all short positions.",
|
|
40
26
|
"properties": {
|
|
41
|
-
"
|
|
27
|
+
"total_short_exposure_weight": { "type": "number" },
|
|
42
28
|
"total_positions_count": { "type": "number" }
|
|
43
29
|
},
|
|
44
|
-
"required": ["
|
|
30
|
+
"required": ["total_short_exposure_weight", "total_positions_count"]
|
|
45
31
|
};
|
|
46
32
|
}
|
|
47
33
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const positions =
|
|
52
|
-
if (!positions || !Array.isArray(positions)) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
34
|
+
process(context) {
|
|
35
|
+
const { extract } = context.math;
|
|
36
|
+
const { user } = context;
|
|
37
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
55
38
|
|
|
56
39
|
for (const pos of positions) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.totalInvestedUsd += pos.Invested || 0;
|
|
40
|
+
if (extract.getDirection(pos) === 'Sell') {
|
|
41
|
+
this.totalExposureWeight += extract.getPositionWeight(pos, user.type);
|
|
60
42
|
this.totalPositions++;
|
|
61
43
|
}
|
|
62
44
|
}
|
|
@@ -64,15 +46,14 @@ class TotalShortFigures {
|
|
|
64
46
|
|
|
65
47
|
getResult() {
|
|
66
48
|
return {
|
|
67
|
-
|
|
49
|
+
total_short_exposure_weight: this.totalExposureWeight,
|
|
68
50
|
total_positions_count: this.totalPositions
|
|
69
51
|
};
|
|
70
52
|
}
|
|
71
53
|
|
|
72
54
|
reset() {
|
|
73
|
-
this.
|
|
55
|
+
this.totalExposureWeight = 0;
|
|
74
56
|
this.totalPositions = 0;
|
|
75
57
|
}
|
|
76
58
|
}
|
|
77
|
-
|
|
78
59
|
module.exports = TotalShortFigures;
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "What is the total $USD value
|
|
5
|
-
* of all short ('sell') positions, grouped by sector?"
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for short figures per sector.
|
|
3
|
+
* REFACTORED: Tracks exposure weight (percentage).
|
|
6
4
|
*/
|
|
7
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
|
-
|
|
9
5
|
class TotalShortPerSector {
|
|
10
6
|
constructor() {
|
|
11
|
-
|
|
12
|
-
this.sectors = new Map();
|
|
13
|
-
// --- STANDARD 0: RENAMED ---
|
|
7
|
+
this.sectorData = new Map();
|
|
14
8
|
this.sectorMap = null;
|
|
15
9
|
}
|
|
16
10
|
|
|
17
|
-
/**
|
|
18
|
-
* Statically defines all metadata for the manifest builder.
|
|
19
|
-
*/
|
|
20
11
|
static getMetadata() {
|
|
21
12
|
return {
|
|
22
13
|
type: 'standard',
|
|
@@ -27,78 +18,65 @@ class TotalShortPerSector {
|
|
|
27
18
|
};
|
|
28
19
|
}
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
* Statically declare dependencies.
|
|
32
|
-
*/
|
|
33
|
-
static getDependencies() {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
36
22
|
|
|
37
|
-
/**
|
|
38
|
-
* Defines the output schema for this calculation.
|
|
39
|
-
*/
|
|
40
23
|
static getSchema() {
|
|
41
|
-
const
|
|
24
|
+
const schema = {
|
|
42
25
|
"type": "object",
|
|
43
26
|
"properties": {
|
|
44
|
-
"
|
|
27
|
+
"total_short_exposure_weight": { "type": "number" },
|
|
45
28
|
"total_positions_count": { "type": "number" }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
"type": "object",
|
|
51
|
-
"description": "Calculates the total $USD value and count of short positions per sector.",
|
|
52
|
-
"patternProperties": { "^.*$": sectorSchema },
|
|
53
|
-
"additionalProperties": sectorSchema
|
|
29
|
+
},
|
|
30
|
+
"required": ["total_short_exposure_weight", "total_positions_count"]
|
|
54
31
|
};
|
|
32
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
55
33
|
}
|
|
56
34
|
|
|
57
|
-
_initSector(
|
|
58
|
-
if (!this.
|
|
59
|
-
this.
|
|
35
|
+
_initSector(sector) {
|
|
36
|
+
if (!this.sectorData.has(sector)) {
|
|
37
|
+
this.sectorData.set(sector, { weight: 0, count: 0 });
|
|
60
38
|
}
|
|
61
39
|
}
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!this.sectorMap)
|
|
67
|
-
this.sectorMap = context.sectorMapping;
|
|
68
|
-
}
|
|
41
|
+
process(context) {
|
|
42
|
+
const { extract } = context.math;
|
|
43
|
+
const { mappings, user } = context;
|
|
44
|
+
if (!this.sectorMap) this.sectorMap = mappings.sectorMapping;
|
|
69
45
|
|
|
70
|
-
const positions =
|
|
71
|
-
if (!positions || !Array.isArray(positions) || !this.sectorMap) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
46
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
74
47
|
|
|
75
48
|
for (const pos of positions) {
|
|
76
|
-
|
|
77
|
-
if (!pos.IsBuy) {
|
|
78
|
-
const instrumentId = pos.InstrumentID;
|
|
79
|
-
if (!instrumentId) continue;
|
|
80
|
-
|
|
81
|
-
// --- STANDARD 0: FIXED ---
|
|
82
|
-
const sectorName = this.sectorMap[instrumentId] || 'N/A';
|
|
83
|
-
this._initSector(sectorName);
|
|
84
|
-
const sectorData = this.sectors.get(sectorName);
|
|
49
|
+
if (extract.getDirection(pos) !== 'Sell') continue;
|
|
85
50
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
51
|
+
const instId = extract.getInstrumentId(pos);
|
|
52
|
+
if (!instId) continue;
|
|
53
|
+
|
|
54
|
+
const sector = this.sectorMap[instId] || 'Unknown';
|
|
55
|
+
const weight = extract.getPositionWeight(pos, user.type);
|
|
56
|
+
|
|
57
|
+
this._initSector(sector);
|
|
58
|
+
const data = this.sectorData.get(sector);
|
|
59
|
+
data.weight += weight;
|
|
60
|
+
data.count++;
|
|
89
61
|
}
|
|
90
62
|
}
|
|
91
63
|
|
|
92
64
|
async getResult() {
|
|
93
|
-
|
|
94
|
-
|
|
65
|
+
const result = {};
|
|
66
|
+
for (const [sector, data] of this.sectorData.entries()) {
|
|
67
|
+
if (data.count > 0) {
|
|
68
|
+
result[sector] = {
|
|
69
|
+
total_short_exposure_weight: data.weight,
|
|
70
|
+
total_positions_count: data.count
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
95
75
|
}
|
|
96
76
|
|
|
97
77
|
reset() {
|
|
98
|
-
this.
|
|
99
|
-
// --- STANDARD 0: RENAMED ---
|
|
78
|
+
this.sectorData.clear();
|
|
100
79
|
this.sectorMap = null;
|
|
101
80
|
}
|
|
102
81
|
}
|
|
103
|
-
|
|
104
82
|
module.exports = TotalShortPerSector;
|