aiden-shared-calculations-unified 1.0.83 → 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 -16
- package/calculations/pyro/risk-appetite-index.js +121 -123
- package/calculations/pyro/squeeze-potential.js +93 -125
- package/calculations/pyro/volatility-signal.js +109 -97
- package/package.json +5 -5
- package/README.MD +0 -155
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator leverage.
|
|
3
3
|
*
|
|
4
|
-
* This metric answers: "For each sector, what is the
|
|
5
|
-
*
|
|
4
|
+
* This metric answers: "For each sector, what is the average
|
|
5
|
+
* leverage used by speculators?"
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
// --- STANDARD 0: REMOVED require('../../utils/sector_mapping_provider') ---
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class SpeculatorLeveragePerSector {
|
|
10
10
|
constructor() {
|
|
11
|
-
// { [
|
|
11
|
+
// { [sectorName]: { sum: 0, count: 0 } }
|
|
12
12
|
this.sectors = new Map();
|
|
13
|
-
|
|
13
|
+
// --- STANDARD 0: RENAMED ---
|
|
14
|
+
this.sectorMap = null;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
// --- NEW ---
|
|
17
17
|
/**
|
|
18
18
|
* Statically defines all metadata for the manifest builder.
|
|
19
19
|
*/
|
|
@@ -22,12 +22,11 @@ class LeveragePerSector {
|
|
|
22
22
|
type: 'standard',
|
|
23
23
|
rootDataDependencies: ['portfolio'],
|
|
24
24
|
isHistorical: false,
|
|
25
|
-
userType: 'speculator',
|
|
25
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
26
26
|
category: 'core_speculator'
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// --- NEW ---
|
|
31
30
|
/**
|
|
32
31
|
* Statically declare dependencies.
|
|
33
32
|
*/
|
|
@@ -37,92 +36,81 @@ class LeveragePerSector {
|
|
|
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
|
-
const
|
|
41
|
+
const sectorSchema = {
|
|
44
42
|
"type": "object",
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"^[0-9inf]+x$": {
|
|
49
|
-
"type": "number",
|
|
50
|
-
"description": "Count of positions at this leverage."
|
|
51
|
-
}
|
|
43
|
+
"properties": {
|
|
44
|
+
"avg_leverage": { "type": "number" },
|
|
45
|
+
"position_count": { "type": "number" }
|
|
52
46
|
},
|
|
53
|
-
"
|
|
47
|
+
"required": ["avg_leverage", "position_count"]
|
|
54
48
|
};
|
|
55
49
|
|
|
56
50
|
return {
|
|
57
51
|
"type": "object",
|
|
58
|
-
"description": "Calculates the
|
|
59
|
-
"patternProperties": {
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
"additionalProperties": leverageSchema
|
|
52
|
+
"description": "Calculates the average leverage used by speculators for each sector.",
|
|
53
|
+
"patternProperties": { "^.*$": sectorSchema },
|
|
54
|
+
"additionalProperties": sectorSchema
|
|
63
55
|
};
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
_initSector(
|
|
67
|
-
if (!this.sectors.has(
|
|
68
|
-
this.sectors.set(
|
|
69
|
-
'1x': 0,
|
|
70
|
-
'2x': 0,
|
|
71
|
-
'5x': 0,
|
|
72
|
-
'10x': 0,
|
|
73
|
-
'inf': 0 // For other leverages
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
_getLeverageBucket(leverage) {
|
|
79
|
-
switch (leverage) {
|
|
80
|
-
case 1: return '1x';
|
|
81
|
-
case 2: return '2x';
|
|
82
|
-
case 5: return '5x';
|
|
83
|
-
case 10: return '10x';
|
|
84
|
-
default: return 'inf';
|
|
58
|
+
_initSector(sectorName) {
|
|
59
|
+
if (!this.sectors.has(sectorName)) {
|
|
60
|
+
this.sectors.set(sectorName, { sum: 0, count: 0 });
|
|
85
61
|
}
|
|
86
62
|
}
|
|
87
63
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
64
|
+
// --- STANDARD 0: UPDATED SIGNATURE ---
|
|
65
|
+
async process(todayPortfolio, yesterdayPortfolio, userId, context) {
|
|
66
|
+
// --- STANDARD 0: FIXED ---
|
|
67
|
+
if (!this.sectorMap) {
|
|
68
|
+
this.sectorMap = context.sectorMapping;
|
|
91
69
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const positions = portfolioData.PublicPositions;
|
|
98
|
-
if (!positions || !Array.isArray(positions) || !this.mappings) {
|
|
70
|
+
|
|
71
|
+
const positions = todayPortfolio.PublicPositions;
|
|
72
|
+
if (!positions || !Array.isArray(positions) || !this.sectorMap) {
|
|
99
73
|
return;
|
|
100
74
|
}
|
|
101
75
|
|
|
102
76
|
for (const pos of positions) {
|
|
103
77
|
const instrumentId = pos.InstrumentID;
|
|
104
|
-
|
|
78
|
+
const leverage = pos.Leverage;
|
|
105
79
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
80
|
+
if (!instrumentId || !leverage || leverage <= 1) {
|
|
81
|
+
continue; // Only care about leveraged positions
|
|
82
|
+
}
|
|
109
83
|
|
|
110
|
-
|
|
111
|
-
const
|
|
84
|
+
// --- STANDARD 0: FIXED ---
|
|
85
|
+
const sectorName = this.sectorMap[instrumentId] || 'N/A';
|
|
86
|
+
this._initSector(sectorName);
|
|
87
|
+
const sectorData = this.sectors.get(sectorName);
|
|
112
88
|
|
|
113
|
-
sectorData
|
|
89
|
+
sectorData.sum += leverage;
|
|
90
|
+
sectorData.count++;
|
|
114
91
|
}
|
|
115
92
|
}
|
|
116
93
|
|
|
117
94
|
async getResult() {
|
|
118
|
-
//
|
|
119
|
-
|
|
95
|
+
// --- STANDARD 0: REMOVED forbidden data load ---
|
|
96
|
+
|
|
97
|
+
const result = {};
|
|
98
|
+
for (const [sectorName, data] of this.sectors.entries()) {
|
|
99
|
+
if (data.count > 0) {
|
|
100
|
+
result[sectorName] = {
|
|
101
|
+
avg_leverage: data.sum / data.count,
|
|
102
|
+
position_count: data.count
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
120
107
|
}
|
|
121
108
|
|
|
122
109
|
reset() {
|
|
123
110
|
this.sectors.clear();
|
|
124
|
-
|
|
111
|
+
// --- STANDARD 0: RENAMED ---
|
|
112
|
+
this.sectorMap = null;
|
|
125
113
|
}
|
|
126
114
|
}
|
|
127
115
|
|
|
128
|
-
module.exports =
|
|
116
|
+
module.exports = SpeculatorLeveragePerSector;
|
|
@@ -1,124 +1,119 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator R/R.
|
|
3
|
+
* --- FIX ---
|
|
4
|
+
* - Rewritten logic to calculate R/R from raw schema fields
|
|
5
|
+
* (OpenRate, StopLossRate, TakeProfitRate) instead of non-existent
|
|
6
|
+
* 'PctTo...' fields.
|
|
7
|
+
* - Updated process signature to match worker.
|
|
6
8
|
*/
|
|
7
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
8
9
|
|
|
9
|
-
class
|
|
10
|
+
class SpeculatorRiskRewardRatioPerAsset {
|
|
10
11
|
constructor() {
|
|
11
|
-
// { [instrumentId]: { sum_rr: 0, count: 0 } }
|
|
12
12
|
this.assets = new Map();
|
|
13
|
-
this.
|
|
13
|
+
this.tickerMap = null;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* Defines the output schema for this calculation.
|
|
18
|
-
* @returns {object} JSON Schema object
|
|
19
|
-
*/
|
|
20
|
-
static getSchema() {
|
|
21
|
-
const tickerSchema = {
|
|
22
|
-
"type": "object",
|
|
23
|
-
"properties": {
|
|
24
|
-
"avg_rr_ratio": {
|
|
25
|
-
"type": "number",
|
|
26
|
-
"description": "Average Risk/Reward ratio (Reward / Risk)."
|
|
27
|
-
},
|
|
28
|
-
"count": {
|
|
29
|
-
"type": "number",
|
|
30
|
-
"description": "Count of positions with both SL and TP set."
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"required": ["avg_rr_ratio", "count"]
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
"type": "object",
|
|
38
|
-
"description": "Calculates the average Risk/Reward ratio from SL/TP settings for each asset.",
|
|
39
|
-
"patternProperties": {
|
|
40
|
-
"^.*$": tickerSchema // Ticker
|
|
41
|
-
},
|
|
42
|
-
"additionalProperties": tickerSchema
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Statically defines all metadata for the manifest builder.
|
|
48
|
-
*/
|
|
49
16
|
static getMetadata() {
|
|
50
17
|
return {
|
|
51
18
|
type: 'standard',
|
|
52
19
|
rootDataDependencies: ['portfolio'],
|
|
53
20
|
isHistorical: false,
|
|
54
|
-
userType: 'speculator',
|
|
21
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
55
22
|
category: 'core_speculator'
|
|
56
23
|
};
|
|
57
24
|
}
|
|
58
25
|
|
|
59
|
-
/**
|
|
60
|
-
* Statically declare dependencies.
|
|
61
|
-
*/
|
|
62
26
|
static getDependencies() {
|
|
63
27
|
return [];
|
|
64
28
|
}
|
|
65
29
|
|
|
30
|
+
static getSchema() {
|
|
31
|
+
const tickerSchema = {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"avg_rr_ratio": { "type": ["number", "null"] },
|
|
35
|
+
"position_count": { "type": "number" }
|
|
36
|
+
},
|
|
37
|
+
"required": ["avg_rr_ratio", "position_count"]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"description": "Calculates the average Risk/Reward ratio at entry for speculators per asset.",
|
|
43
|
+
"patternProperties": { "^.*$": tickerSchema },
|
|
44
|
+
"additionalProperties": tickerSchema
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
66
48
|
_initAsset(instrumentId) {
|
|
67
49
|
if (!this.assets.has(instrumentId)) {
|
|
68
|
-
this.assets.set(instrumentId, {
|
|
50
|
+
this.assets.set(instrumentId, { sum: 0, count: 0 });
|
|
69
51
|
}
|
|
70
52
|
}
|
|
71
53
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
54
|
+
// --- THIS IS THE FIX ---
|
|
55
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
56
|
+
if (!this.tickerMap) {
|
|
57
|
+
this.tickerMap = context.instrumentToTicker;
|
|
75
58
|
}
|
|
76
|
-
|
|
77
|
-
const positions =
|
|
59
|
+
|
|
60
|
+
const positions = todayPortfolio.PublicPositions;
|
|
78
61
|
if (!positions || !Array.isArray(positions)) {
|
|
79
62
|
return;
|
|
80
63
|
}
|
|
81
64
|
|
|
82
65
|
for (const pos of positions) {
|
|
83
66
|
const instrumentId = pos.InstrumentID;
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
continue;
|
|
67
|
+
const slRate = pos.StopLossRate;
|
|
68
|
+
const tpRate = pos.TakeProfitRate;
|
|
69
|
+
const openRate = pos.OpenRate;
|
|
70
|
+
|
|
71
|
+
if (!instrumentId || !slRate || slRate <= 0 || !tpRate || tpRate <= 0 || !openRate || openRate <= 0) {
|
|
72
|
+
continue; // Must have SL, TP, and OpenRate
|
|
91
73
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
74
|
+
|
|
75
|
+
let risk = 0;
|
|
76
|
+
let reward = 0;
|
|
77
|
+
|
|
78
|
+
if (pos.IsBuy) {
|
|
79
|
+
// Long
|
|
80
|
+
risk = openRate - slRate;
|
|
81
|
+
reward = tpRate - openRate;
|
|
82
|
+
} else {
|
|
83
|
+
// Short
|
|
84
|
+
risk = slRate - openRate;
|
|
85
|
+
reward = openRate - tpRate;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// If risk or reward is negative/zero, it's an invalid R/R setup
|
|
89
|
+
if (risk <= 0 || reward <= 0) {
|
|
90
|
+
continue;
|
|
98
91
|
}
|
|
99
|
-
|
|
100
|
-
const rr_ratio = reward / risk;
|
|
101
|
-
|
|
92
|
+
|
|
102
93
|
this._initAsset(instrumentId);
|
|
103
94
|
const assetData = this.assets.get(instrumentId);
|
|
104
|
-
|
|
95
|
+
|
|
96
|
+
const rrRatio = reward / risk;
|
|
97
|
+
|
|
98
|
+
assetData.sum += rrRatio;
|
|
105
99
|
assetData.count++;
|
|
106
100
|
}
|
|
107
101
|
}
|
|
102
|
+
// --- END FIX ---
|
|
108
103
|
|
|
109
104
|
async getResult() {
|
|
110
|
-
if (!this.
|
|
111
|
-
|
|
105
|
+
if (!this.tickerMap) {
|
|
106
|
+
return {};
|
|
112
107
|
}
|
|
113
108
|
|
|
114
109
|
const result = {};
|
|
115
110
|
for (const [instrumentId, data] of this.assets.entries()) {
|
|
116
|
-
const ticker = this.
|
|
117
|
-
|
|
111
|
+
const ticker = this.tickerMap[instrumentId] || `id_${instrumentId}`;
|
|
112
|
+
|
|
118
113
|
if (data.count > 0) {
|
|
119
114
|
result[ticker] = {
|
|
120
|
-
avg_rr_ratio: data.
|
|
121
|
-
|
|
115
|
+
avg_rr_ratio: data.sum / data.count,
|
|
116
|
+
position_count: data.count
|
|
122
117
|
};
|
|
123
118
|
}
|
|
124
119
|
}
|
|
@@ -127,8 +122,8 @@ class RiskRewardRatioPerAsset {
|
|
|
127
122
|
|
|
128
123
|
reset() {
|
|
129
124
|
this.assets.clear();
|
|
130
|
-
this.
|
|
125
|
+
this.tickerMap = null;
|
|
131
126
|
}
|
|
132
127
|
}
|
|
133
128
|
|
|
134
|
-
module.exports =
|
|
129
|
+
module.exports = SpeculatorRiskRewardRatioPerAsset;
|
|
@@ -1,140 +1,119 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 1) for speculator
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* @fileoverview Calculation (Pass 1) for speculator SL distance.
|
|
3
|
+
* --- FIX ---
|
|
4
|
+
* - Rewritten logic to calculate SL distance from raw schema fields
|
|
5
|
+
* (StopLossRate, CurrentRate) instead of 'PctToStopLoss'.
|
|
6
|
+
* - Updated process signature to match worker.
|
|
7
7
|
*/
|
|
8
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
9
8
|
|
|
10
|
-
class
|
|
9
|
+
class SpeculatorSLDistanceBySectorShortLong {
|
|
11
10
|
constructor() {
|
|
12
|
-
// { [sector]: { long_pct: [], long_val: [], short_pct: [], short_val: [] } }
|
|
13
11
|
this.sectors = new Map();
|
|
14
|
-
this.
|
|
12
|
+
this.sectorMap = null;
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
// --- NEW ---
|
|
18
|
-
/**
|
|
19
|
-
* Statically defines all metadata for the manifest builder.
|
|
20
|
-
*/
|
|
21
15
|
static getMetadata() {
|
|
22
16
|
return {
|
|
23
17
|
type: 'standard',
|
|
24
18
|
rootDataDependencies: ['portfolio'],
|
|
25
19
|
isHistorical: false,
|
|
26
|
-
userType: 'speculator',
|
|
20
|
+
userType: 'speculator', // <-- KEY: Only runs for speculators
|
|
27
21
|
category: 'core_speculator'
|
|
28
22
|
};
|
|
29
23
|
}
|
|
30
24
|
|
|
31
|
-
// --- NEW ---
|
|
32
|
-
/**
|
|
33
|
-
* Statically declare dependencies.
|
|
34
|
-
*/
|
|
35
25
|
static getDependencies() {
|
|
36
26
|
return [];
|
|
37
27
|
}
|
|
38
28
|
|
|
39
|
-
/**
|
|
40
|
-
* Defines the output schema for this calculation.
|
|
41
|
-
* @returns {object} JSON Schema object
|
|
42
|
-
*/
|
|
43
29
|
static getSchema() {
|
|
44
30
|
const sectorSchema = {
|
|
45
31
|
"type": "object",
|
|
46
32
|
"properties": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
"short_count": { "type": "number" }
|
|
53
|
-
},
|
|
54
|
-
"required": ["long_avg_dist_pct", "long_avg_dist_val", "long_count", "short_avg_dist_pct", "short_avg_dist_val", "short_count"]
|
|
33
|
+
"long_avg_distance_pct": { "type": "number" },
|
|
34
|
+
"long_position_count": { "type": "number" },
|
|
35
|
+
"short_avg_distance_pct": { "type": "number" },
|
|
36
|
+
"short_position_count": { "type": "number" }
|
|
37
|
+
}
|
|
55
38
|
};
|
|
56
39
|
|
|
57
40
|
return {
|
|
58
41
|
"type": "object",
|
|
59
|
-
"description": "Calculates
|
|
60
|
-
"patternProperties": {
|
|
61
|
-
"^.*$": sectorSchema // Sector
|
|
62
|
-
},
|
|
42
|
+
"description": "Calculates the average distance to SL, broken down by long/short, per sector.",
|
|
43
|
+
"patternProperties": { "^.*$": sectorSchema },
|
|
63
44
|
"additionalProperties": sectorSchema
|
|
64
45
|
};
|
|
65
46
|
}
|
|
66
47
|
|
|
67
|
-
_initSector(
|
|
68
|
-
if (!this.sectors.has(
|
|
69
|
-
this.sectors.set(
|
|
70
|
-
long_pct: [], long_val: [], short_pct: [], short_val: []
|
|
71
|
-
});
|
|
48
|
+
_initSector(sectorName) {
|
|
49
|
+
if (!this.sectors.has(sectorName)) {
|
|
50
|
+
this.sectors.set(sectorName, { long_sum: 0, long_count: 0, short_sum: 0, short_count: 0 });
|
|
72
51
|
}
|
|
73
52
|
}
|
|
74
|
-
|
|
75
|
-
_avg(arr) {
|
|
76
|
-
if (arr.length === 0) return 0;
|
|
77
|
-
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
78
|
-
}
|
|
79
53
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
54
|
+
// --- THIS IS THE FIX ---
|
|
55
|
+
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
56
|
+
if (!this.sectorMap) {
|
|
57
|
+
this.sectorMap = context.sectorMapping;
|
|
83
58
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const positions = portfolioData.PublicPositions;
|
|
89
|
-
if (!positions || !Array.isArray(positions) || !this.mappings) {
|
|
59
|
+
|
|
60
|
+
const positions = todayPortfolio.PublicPositions;
|
|
61
|
+
if (!positions || !Array.isArray(positions) || !this.sectorMap) {
|
|
90
62
|
return;
|
|
91
63
|
}
|
|
92
64
|
|
|
93
65
|
for (const pos of positions) {
|
|
94
66
|
const instrumentId = pos.InstrumentID;
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
if (!instrumentId ||
|
|
99
|
-
continue;
|
|
67
|
+
const slRate = pos.StopLossRate;
|
|
68
|
+
const currentRate = pos.CurrentRate;
|
|
69
|
+
|
|
70
|
+
if (!instrumentId || !slRate || slRate <= 0 || !currentRate || currentRate <= 0) {
|
|
71
|
+
continue; // No SL or invalid data
|
|
100
72
|
}
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
this._initSector(
|
|
104
|
-
const sectorData = this.sectors.get(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const distance_pct = (distance_val / open_rate) * 100;
|
|
108
|
-
|
|
73
|
+
|
|
74
|
+
const sectorName = this.sectorMap[instrumentId] || 'N/A';
|
|
75
|
+
this._initSector(sectorName);
|
|
76
|
+
const sectorData = this.sectors.get(sectorName);
|
|
77
|
+
|
|
78
|
+
let pctToSL = 0;
|
|
109
79
|
if (pos.IsBuy) {
|
|
110
|
-
|
|
111
|
-
|
|
80
|
+
// Long: (Current - SL) / Current
|
|
81
|
+
pctToSL = (currentRate - slRate) / currentRate;
|
|
82
|
+
if (pctToSL > 0) {
|
|
83
|
+
sectorData.long_sum += (pctToSL * 100);
|
|
84
|
+
sectorData.long_count++;
|
|
85
|
+
}
|
|
112
86
|
} else {
|
|
113
|
-
|
|
114
|
-
|
|
87
|
+
// Short: (SL - Current) / Current
|
|
88
|
+
pctToSL = (slRate - currentRate) / currentRate;
|
|
89
|
+
if (pctToSL > 0) {
|
|
90
|
+
sectorData.short_sum += (pctToSL * 100);
|
|
91
|
+
sectorData.short_count++;
|
|
92
|
+
}
|
|
115
93
|
}
|
|
116
94
|
}
|
|
117
95
|
}
|
|
96
|
+
// --- END FIX ---
|
|
118
97
|
|
|
119
98
|
async getResult() {
|
|
120
99
|
const result = {};
|
|
121
|
-
for (const [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
100
|
+
for (const [sectorName, data] of this.sectors.entries()) {
|
|
101
|
+
if (data.long_count > 0 || data.short_count > 0) {
|
|
102
|
+
result[sectorName] = {
|
|
103
|
+
long_avg_distance_pct: (data.long_count > 0) ? (data.long_sum / data.long_count) : 0,
|
|
104
|
+
long_position_count: data.long_count,
|
|
105
|
+
short_avg_distance_pct: (data.short_count > 0) ? (data.short_sum / data.short_count) : 0,
|
|
106
|
+
short_position_count: data.short_count
|
|
107
|
+
};
|
|
108
|
+
}
|
|
130
109
|
}
|
|
131
110
|
return result;
|
|
132
111
|
}
|
|
133
112
|
|
|
134
113
|
reset() {
|
|
135
114
|
this.sectors.clear();
|
|
136
|
-
this.
|
|
115
|
+
this.sectorMap = null;
|
|
137
116
|
}
|
|
138
117
|
}
|
|
139
118
|
|
|
140
|
-
module.exports =
|
|
119
|
+
module.exports = SpeculatorSLDistanceBySectorShortLong;
|