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,22 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Calculation (Pass 1) for profitability ratio per stock.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "For each stock, what is the count of
|
|
5
|
-
* profitable versus unprofitable positions?"
|
|
3
|
+
* REFACTORED: Counts (No USD involved).
|
|
6
4
|
*/
|
|
7
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
|
-
|
|
9
5
|
class ProfitabilityRatioPerStock {
|
|
10
6
|
constructor() {
|
|
11
|
-
|
|
12
|
-
this.assets = new Map();
|
|
13
|
-
// --- STANDARD 0: RENAMED ---
|
|
7
|
+
this.stockData = new Map();
|
|
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',
|
|
@@ -27,102 +18,57 @@ class ProfitabilityRatioPerStock {
|
|
|
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 stockSchema = {
|
|
42
25
|
"type": "object",
|
|
43
|
-
"description": "Profit/loss position counts for a specific asset.",
|
|
44
26
|
"properties": {
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
},
|
|
49
|
-
"unprofitable_count": {
|
|
50
|
-
"type": "number",
|
|
51
|
-
"description": "Count of positions in loss."
|
|
52
|
-
},
|
|
53
|
-
"ratio": {
|
|
54
|
-
"type": ["number", "null"],
|
|
55
|
-
"description": "Ratio of profitable to unprofitable (Profit / Loss). Null if no losing positions."
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"required": ["profitable_count", "unprofitable_count", "ratio"]
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
"type": "object",
|
|
63
|
-
"description": "Calculates the count of profitable vs. unprofitable positions for each asset.",
|
|
64
|
-
"patternProperties": {
|
|
65
|
-
"^.*$": tickerSchema // Ticker
|
|
27
|
+
"profitability_ratio": { "type": ["number", "null"] },
|
|
28
|
+
"profitable_count": { "type": "number" },
|
|
29
|
+
"unprofitable_count": { "type": "number" }
|
|
66
30
|
},
|
|
67
|
-
"
|
|
31
|
+
"required": ["profitability_ratio", "profitable_count", "unprofitable_count"]
|
|
68
32
|
};
|
|
33
|
+
return { "type": "object", "patternProperties": { "^.*$": stockSchema } };
|
|
69
34
|
}
|
|
70
35
|
|
|
71
|
-
|
|
72
|
-
if (!this.
|
|
73
|
-
this.
|
|
74
|
-
profitable: 0,
|
|
75
|
-
unprofitable: 0
|
|
76
|
-
});
|
|
36
|
+
_initStock(instId) {
|
|
37
|
+
if (!this.stockData.has(instId)) {
|
|
38
|
+
this.stockData.set(instId, { profitable: 0, unprofitable: 0 });
|
|
77
39
|
}
|
|
78
40
|
}
|
|
79
41
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (!this.tickerMap)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const positions = todayPortfolio.PublicPositions || todayPortfolio.AggregatedPositions;
|
|
88
|
-
if (!positions || !Array.isArray(positions)) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
42
|
+
process(context) {
|
|
43
|
+
const { extract } = context.math;
|
|
44
|
+
const { mappings, user } = context;
|
|
45
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
46
|
+
|
|
47
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
91
48
|
|
|
92
49
|
for (const pos of positions) {
|
|
93
|
-
const
|
|
94
|
-
if (!
|
|
95
|
-
|
|
96
|
-
this._initAsset(instrumentId);
|
|
97
|
-
const assetData = this.assets.get(instrumentId);
|
|
98
|
-
const pnl = pos.NetProfit || 0;
|
|
50
|
+
const instId = extract.getInstrumentId(pos);
|
|
51
|
+
if (!instId) continue;
|
|
99
52
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
53
|
+
const pnl = extract.getNetProfit(pos);
|
|
54
|
+
this._initStock(instId);
|
|
55
|
+
const data = this.stockData.get(instId);
|
|
56
|
+
|
|
57
|
+
if (pnl > 0) data.profitable++;
|
|
58
|
+
else if (pnl < 0) data.unprofitable++;
|
|
105
59
|
}
|
|
106
60
|
}
|
|
107
61
|
|
|
108
62
|
async getResult() {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// Failsafe check
|
|
112
|
-
if (!this.tickerMap) {
|
|
113
|
-
return {}; // process() must run first
|
|
114
|
-
}
|
|
115
|
-
|
|
63
|
+
if (!this.tickerMap) return {};
|
|
116
64
|
const result = {};
|
|
117
|
-
for (const [
|
|
118
|
-
|
|
119
|
-
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
120
|
-
|
|
65
|
+
for (const [instId, data] of this.stockData.entries()) {
|
|
66
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
121
67
|
if (data.profitable > 0 || data.unprofitable > 0) {
|
|
122
68
|
result[ticker] = {
|
|
69
|
+
profitability_ratio: (data.unprofitable > 0) ? (data.profitable / data.unprofitable) : null,
|
|
123
70
|
profitable_count: data.profitable,
|
|
124
|
-
unprofitable_count: data.unprofitable
|
|
125
|
-
ratio: (data.unprofitable > 0) ? (data.profitable / data.unprofitable) : null
|
|
71
|
+
unprofitable_count: data.unprofitable
|
|
126
72
|
};
|
|
127
73
|
}
|
|
128
74
|
}
|
|
@@ -130,10 +76,8 @@ class ProfitabilityRatioPerStock {
|
|
|
130
76
|
}
|
|
131
77
|
|
|
132
78
|
reset() {
|
|
133
|
-
this.
|
|
134
|
-
// --- STANDARD 0: RENAMED ---
|
|
79
|
+
this.stockData.clear();
|
|
135
80
|
this.tickerMap = null;
|
|
136
81
|
}
|
|
137
82
|
}
|
|
138
|
-
|
|
139
83
|
module.exports = ProfitabilityRatioPerStock;
|
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for profitability skew.
|
|
3
|
-
*
|
|
4
|
-
_ This metric answers: "For each stock, what is the sum and
|
|
5
|
-
* count of *profits* and *losses*?"
|
|
6
|
-
*
|
|
7
|
-
* This helps determine if returns are skewed (e.g., many small
|
|
8
|
-
* wins and a few huge losses).
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for profitability skew per stock.
|
|
3
|
+
* REFACTORED: Uses percentages.
|
|
9
4
|
*/
|
|
10
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
11
|
-
|
|
12
5
|
class ProfitabilitySkewPerStock {
|
|
13
6
|
constructor() {
|
|
14
|
-
|
|
15
|
-
this.assets = new Map();
|
|
16
|
-
// --- STANDARD 0: RENAMED ---
|
|
7
|
+
this.stockData = new Map();
|
|
17
8
|
this.tickerMap = null;
|
|
18
9
|
}
|
|
19
10
|
|
|
20
|
-
/**
|
|
21
|
-
* Statically defines all metadata for the manifest builder.
|
|
22
|
-
*/
|
|
23
11
|
static getMetadata() {
|
|
24
12
|
return {
|
|
25
13
|
type: 'standard',
|
|
@@ -30,116 +18,75 @@ class ProfitabilitySkewPerStock {
|
|
|
30
18
|
};
|
|
31
19
|
}
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
* Statically declare dependencies.
|
|
35
|
-
*/
|
|
36
|
-
static getDependencies() {
|
|
37
|
-
return [];
|
|
38
|
-
}
|
|
21
|
+
static getDependencies() { return []; }
|
|
39
22
|
|
|
40
|
-
/**
|
|
41
|
-
* Defines the output schema for this calculation.
|
|
42
|
-
*/
|
|
43
23
|
static getSchema() {
|
|
44
|
-
const
|
|
24
|
+
const stockSchema = {
|
|
45
25
|
"type": "object",
|
|
46
|
-
"description": "P&L skew metrics for a specific asset.",
|
|
47
26
|
"properties": {
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"loss_count": { "type": "number" },
|
|
52
|
-
"skew_ratio": {
|
|
53
|
-
"type": ["number", "null"],
|
|
54
|
-
"description": "Ratio of (Average Profit / Average Loss). Null if no losses."
|
|
55
|
-
}
|
|
27
|
+
"avg_profit_pct": { "type": "number" },
|
|
28
|
+
"avg_loss_pct": { "type": "number" },
|
|
29
|
+
"skew_ratio": { "type": ["number", "null"] }
|
|
56
30
|
},
|
|
57
|
-
"required": ["
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
"type": "object",
|
|
62
|
-
"description": "Calculates the skew of returns (avg profit vs. avg loss) for each asset.",
|
|
63
|
-
"patternProperties": {
|
|
64
|
-
"^.*$": tickerSchema // Ticker
|
|
65
|
-
},
|
|
66
|
-
"additionalProperties": tickerSchema
|
|
31
|
+
"required": ["avg_profit_pct", "avg_loss_pct", "skew_ratio"]
|
|
67
32
|
};
|
|
33
|
+
return { "type": "object", "patternProperties": { "^.*$": stockSchema } };
|
|
68
34
|
}
|
|
69
35
|
|
|
70
|
-
|
|
71
|
-
if (!this.
|
|
72
|
-
this.
|
|
73
|
-
profit_sum: 0,
|
|
74
|
-
profit_count: 0,
|
|
75
|
-
loss_sum: 0, // Will be negative
|
|
76
|
-
loss_count: 0
|
|
77
|
-
});
|
|
36
|
+
_initStock(instId) {
|
|
37
|
+
if (!this.stockData.has(instId)) {
|
|
38
|
+
this.stockData.set(instId, { profitSumPct: 0, profitCount: 0, lossSumPct: 0, lossCount: 0 });
|
|
78
39
|
}
|
|
79
40
|
}
|
|
80
41
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (!this.tickerMap)
|
|
85
|
-
this.tickerMap = context.instrumentToTicker;
|
|
86
|
-
}
|
|
42
|
+
process(context) {
|
|
43
|
+
const { extract } = context.math;
|
|
44
|
+
const { mappings, user } = context;
|
|
45
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
87
46
|
|
|
88
|
-
const positions =
|
|
89
|
-
if (!positions || !Array.isArray(positions)) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
47
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
92
48
|
|
|
93
49
|
for (const pos of positions) {
|
|
94
|
-
const
|
|
95
|
-
if (!
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
50
|
+
const instId = extract.getInstrumentId(pos);
|
|
51
|
+
if (!instId) continue;
|
|
52
|
+
|
|
53
|
+
const pnl = extract.getNetProfit(pos);
|
|
54
|
+
if (pnl === 0) continue;
|
|
55
|
+
|
|
56
|
+
this._initStock(instId);
|
|
57
|
+
const data = this.stockData.get(instId);
|
|
100
58
|
|
|
101
59
|
if (pnl > 0) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
} else
|
|
105
|
-
|
|
106
|
-
|
|
60
|
+
data.profitSumPct += pnl;
|
|
61
|
+
data.profitCount++;
|
|
62
|
+
} else {
|
|
63
|
+
data.lossSumPct += Math.abs(pnl);
|
|
64
|
+
data.lossCount++;
|
|
107
65
|
}
|
|
108
66
|
}
|
|
109
67
|
}
|
|
110
68
|
|
|
111
69
|
async getResult() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Failsafe check
|
|
115
|
-
if (!this.tickerMap) {
|
|
116
|
-
return {}; // process() must run first
|
|
117
|
-
}
|
|
118
|
-
|
|
70
|
+
if (!this.tickerMap) return {};
|
|
119
71
|
const result = {};
|
|
120
|
-
for (const [
|
|
121
|
-
|
|
122
|
-
|
|
72
|
+
for (const [instId, data] of this.stockData.entries()) {
|
|
73
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
74
|
+
|
|
75
|
+
const avgProfit = data.profitCount > 0 ? data.profitSumPct / data.profitCount : 0;
|
|
76
|
+
const avgLoss = data.lossCount > 0 ? data.lossSumPct / data.lossCount : 0;
|
|
123
77
|
|
|
124
|
-
const avgProfit = (data.profit_count > 0) ? (data.profit_sum / data.profit_count) : 0;
|
|
125
|
-
const avgLoss = (data.loss_count > 0) ? (Math.abs(data.loss_sum) / data.loss_count) : 0;
|
|
126
|
-
|
|
127
78
|
result[ticker] = {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
loss_count: data.loss_count,
|
|
132
|
-
skew_ratio: (avgLoss > 0) ? (avgProfit / avgLoss) : null
|
|
79
|
+
avg_profit_pct: avgProfit,
|
|
80
|
+
avg_loss_pct: avgLoss,
|
|
81
|
+
skew_ratio: avgLoss > 0 ? avgProfit / avgLoss : null
|
|
133
82
|
};
|
|
134
83
|
}
|
|
135
84
|
return result;
|
|
136
85
|
}
|
|
137
86
|
|
|
138
87
|
reset() {
|
|
139
|
-
this.
|
|
140
|
-
// --- STANDARD 0: RENAMED ---
|
|
88
|
+
this.stockData.clear();
|
|
141
89
|
this.tickerMap = null;
|
|
142
90
|
}
|
|
143
91
|
}
|
|
144
|
-
|
|
145
92
|
module.exports = ProfitabilitySkewPerStock;
|
|
@@ -1,113 +1,81 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1)
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "How many users have an *overall portfolio*
|
|
5
|
-
* in profit versus in loss today?"
|
|
2
|
+
* @fileoverview Calculation (Pass 1) - Bucket users by P&L status.
|
|
3
|
+
* REFACTORED: Standardized extraction.
|
|
6
4
|
*/
|
|
7
5
|
class ProfitableAndUnprofitableStatus {
|
|
8
6
|
constructor() {
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
7
|
+
this.stockData = new Map();
|
|
8
|
+
this.tickerMap = null;
|
|
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',
|
|
19
|
-
rootDataDependencies: ['portfolio'],
|
|
14
|
+
rootDataDependencies: ['portfolio'],
|
|
20
15
|
isHistorical: false,
|
|
21
16
|
userType: 'all',
|
|
22
17
|
category: 'core_pnl'
|
|
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
|
+
const schema = {
|
|
38
25
|
"type": "object",
|
|
39
|
-
"description": "Tracks the count of users whose *overall portfolio* is in profit vs. in loss.",
|
|
40
26
|
"properties": {
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
"total_in_loss": {
|
|
46
|
-
"type": "number",
|
|
47
|
-
"description": "Count of users with a total portfolio P&L < 0."
|
|
48
|
-
},
|
|
49
|
-
"profit_ratio_pct": {
|
|
50
|
-
"type": "number",
|
|
51
|
-
"description": "Percentage of users in profit (In Profit / Total)."
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"required": ["total_in_profit", "total_in_loss", "profit_ratio_pct"]
|
|
27
|
+
"profitable_users": { "type": "array", "items": { "type": "string" } },
|
|
28
|
+
"unprofitable_users": { "type": "array", "items": { "type": "string" } }
|
|
29
|
+
}
|
|
55
30
|
};
|
|
31
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
56
32
|
}
|
|
57
33
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// ---
|
|
62
|
-
// FIX: The 'data-generator.js' does not provide a 'Summary' object
|
|
63
|
-
// or a root-level 'NetProfit' field.
|
|
64
|
-
// We must derive the overall P&L from the individual positions,
|
|
65
|
-
// which *are* generated.
|
|
66
|
-
// ---
|
|
67
|
-
const positions = todayPortfolio.AggregatedPositions;
|
|
68
|
-
if (!positions || !Array.isArray(positions) || positions.length === 0) {
|
|
69
|
-
return; // No positions, no P&L status
|
|
34
|
+
_initStock(instId) {
|
|
35
|
+
if (!this.stockData.has(instId)) {
|
|
36
|
+
this.stockData.set(instId, { profitable: [], unprofitable: [] });
|
|
70
37
|
}
|
|
38
|
+
}
|
|
71
39
|
|
|
72
|
-
|
|
73
|
-
|
|
40
|
+
process(context) {
|
|
41
|
+
const { extract } = context.math;
|
|
42
|
+
const { mappings, user } = context;
|
|
43
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
74
44
|
|
|
75
|
-
|
|
76
|
-
const invested = pos.Invested;
|
|
77
|
-
const pnl = pos.NetProfit;
|
|
45
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
78
46
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
47
|
+
for (const pos of positions) {
|
|
48
|
+
const instId = extract.getInstrumentId(pos);
|
|
49
|
+
const pnl = extract.getNetProfit(pos);
|
|
50
|
+
|
|
51
|
+
if (!instId || pnl === 0) continue;
|
|
84
52
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
53
|
+
this._initStock(instId);
|
|
54
|
+
const data = this.stockData.get(instId);
|
|
88
55
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (overallPnl > 0) {
|
|
92
|
-
this.total_in_profit++;
|
|
93
|
-
} else if (overallPnl < 0) {
|
|
94
|
-
this.total_in_loss++;
|
|
56
|
+
if (pnl > 0) data.profitable.push(user.id);
|
|
57
|
+
else data.unprofitable.push(user.id);
|
|
95
58
|
}
|
|
96
59
|
}
|
|
97
60
|
|
|
98
|
-
getResult() {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
61
|
+
async getResult() {
|
|
62
|
+
if (!this.tickerMap) return {};
|
|
63
|
+
const result = {};
|
|
64
|
+
for (const [instId, data] of this.stockData.entries()) {
|
|
65
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
66
|
+
if (data.profitable.length > 0 || data.unprofitable.length > 0) {
|
|
67
|
+
result[ticker] = {
|
|
68
|
+
profitable_users: data.profitable,
|
|
69
|
+
unprofitable_users: data.unprofitable
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
105
74
|
}
|
|
106
75
|
|
|
107
76
|
reset() {
|
|
108
|
-
this.
|
|
109
|
-
this.
|
|
77
|
+
this.stockData.clear();
|
|
78
|
+
this.tickerMap = null;
|
|
110
79
|
}
|
|
111
80
|
}
|
|
112
|
-
|
|
113
81
|
module.exports = ProfitableAndUnprofitableStatus;
|
|
@@ -1,56 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Calculation (Pass 1) for sentiment per stock.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "For each stock, what is the count
|
|
5
|
-
* of long versus short positions?"
|
|
3
|
+
* REFACTORED: Counts Long vs Short.
|
|
6
4
|
*/
|
|
7
|
-
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
|
-
|
|
9
5
|
class SentimentPerStock {
|
|
10
6
|
constructor() {
|
|
11
|
-
// { [instrumentId]: { long: 0, short: 0 } }
|
|
12
7
|
this.assets = new Map();
|
|
13
|
-
// --- STANDARD 0: RENAMED ---
|
|
14
8
|
this.tickerMap = null;
|
|
15
9
|
}
|
|
16
10
|
|
|
17
|
-
/**
|
|
18
|
-
* Defines the output schema for this calculation.
|
|
19
|
-
*/
|
|
20
11
|
static getSchema() {
|
|
21
12
|
const tickerSchema = {
|
|
22
13
|
"type": "object",
|
|
23
|
-
"description": "Long/short position counts for a specific asset.",
|
|
24
14
|
"properties": {
|
|
25
|
-
"long_count": {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
"short_count": {
|
|
30
|
-
"type": "number",
|
|
31
|
-
"description": "Count of short ('sell') positions."
|
|
32
|
-
},
|
|
33
|
-
"sentiment_ratio": {
|
|
34
|
-
"type": ["number", "null"],
|
|
35
|
-
"description": "Ratio of long to short (Long / Short). Null if no short positions."
|
|
36
|
-
}
|
|
15
|
+
"long_count": { "type": "number" },
|
|
16
|
+
"short_count": { "type": "number" },
|
|
17
|
+
"sentiment_ratio": { "type": ["number", "null"] }
|
|
37
18
|
},
|
|
38
19
|
"required": ["long_count", "short_count", "sentiment_ratio"]
|
|
39
20
|
};
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
"type": "object",
|
|
43
|
-
"description": "Calculates the count of long vs. short positions for each asset.",
|
|
44
|
-
"patternProperties": {
|
|
45
|
-
"^.*$": tickerSchema // Ticker
|
|
46
|
-
},
|
|
47
|
-
"additionalProperties": tickerSchema
|
|
48
|
-
};
|
|
21
|
+
return { "type": "object", "patternProperties": { "^.*$": tickerSchema } };
|
|
49
22
|
}
|
|
50
23
|
|
|
51
|
-
/**
|
|
52
|
-
* Statically defines all metadata for the manifest builder.
|
|
53
|
-
*/
|
|
54
24
|
static getMetadata() {
|
|
55
25
|
return {
|
|
56
26
|
type: 'standard',
|
|
@@ -61,62 +31,41 @@ class SentimentPerStock {
|
|
|
61
31
|
};
|
|
62
32
|
}
|
|
63
33
|
|
|
64
|
-
|
|
65
|
-
* Statically declare dependencies.
|
|
66
|
-
*/
|
|
67
|
-
static getDependencies() {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
34
|
+
static getDependencies() { return []; }
|
|
70
35
|
|
|
71
36
|
_initAsset(instrumentId) {
|
|
72
37
|
if (!this.assets.has(instrumentId)) {
|
|
73
|
-
this.assets.set(instrumentId, {
|
|
74
|
-
long: 0,
|
|
75
|
-
short: 0
|
|
76
|
-
});
|
|
38
|
+
this.assets.set(instrumentId, { long: 0, short: 0 });
|
|
77
39
|
}
|
|
78
40
|
}
|
|
79
41
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
42
|
+
process(context) {
|
|
43
|
+
const { extract } = context.math;
|
|
44
|
+
const { mappings, user } = context;
|
|
45
|
+
// Capture mapping reference for getResult
|
|
46
|
+
if (!this.tickerMap) this.tickerMap = mappings.instrumentToTicker;
|
|
86
47
|
|
|
87
|
-
const positions =
|
|
88
|
-
if (!positions || !Array.isArray(positions)) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
48
|
+
const positions = extract.getPositions(user.portfolio.today, user.type);
|
|
91
49
|
|
|
92
50
|
for (const pos of positions) {
|
|
93
|
-
const
|
|
94
|
-
if (!
|
|
51
|
+
const instId = extract.getInstrumentId(pos);
|
|
52
|
+
if (!instId) continue;
|
|
95
53
|
|
|
96
|
-
this._initAsset(
|
|
97
|
-
const assetData = this.assets.get(
|
|
54
|
+
this._initAsset(instId);
|
|
55
|
+
const assetData = this.assets.get(instId);
|
|
56
|
+
const direction = extract.getDirection(pos);
|
|
98
57
|
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
} else {
|
|
102
|
-
assetData.short++;
|
|
103
|
-
}
|
|
58
|
+
if (direction === 'Buy') assetData.long++;
|
|
59
|
+
else assetData.short++;
|
|
104
60
|
}
|
|
105
61
|
}
|
|
106
62
|
|
|
107
63
|
async getResult() {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// Failsafe check
|
|
111
|
-
if (!this.tickerMap) {
|
|
112
|
-
return {}; // process() must run first
|
|
113
|
-
}
|
|
114
|
-
|
|
64
|
+
if (!this.tickerMap) return {};
|
|
65
|
+
|
|
115
66
|
const result = {};
|
|
116
|
-
for (const [
|
|
117
|
-
|
|
118
|
-
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
119
|
-
|
|
67
|
+
for (const [instId, data] of this.assets.entries()) {
|
|
68
|
+
const ticker = this.tickerMap[instId] || `id_${instId}`;
|
|
120
69
|
if (data.long > 0 || data.short > 0) {
|
|
121
70
|
result[ticker] = {
|
|
122
71
|
long_count: data.long,
|
|
@@ -130,9 +79,7 @@ class SentimentPerStock {
|
|
|
130
79
|
|
|
131
80
|
reset() {
|
|
132
81
|
this.assets.clear();
|
|
133
|
-
// --- STANDARD 0: RENAMED ---
|
|
134
82
|
this.tickerMap = null;
|
|
135
83
|
}
|
|
136
84
|
}
|
|
137
|
-
|
|
138
85
|
module.exports = SentimentPerStock;
|