aiden-shared-calculations-unified 1.0.8 → 1.0.9
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/pnl/average_daily_pnl_all_users.js +1 -1
- package/calculations/pnl/average_daily_pnl_per_sector.js +1 -1
- package/calculations/pnl/average_daily_pnl_per_stock.js +1 -1
- package/calculations/pnl/average_daily_position_pnl.js +1 -1
- package/calculations/pnl/pnl_distribution_per_stock.js +52 -52
- package/calculations/pnl/profitability_ratio_per_stock.js +50 -50
- package/calculations/pnl/profitability_skew_per_stock.js +58 -58
- package/calculations/sanity/users_processed.js +26 -26
- package/calculations/sectors/total_long_per_sector.js +1 -1
- package/calculations/sectors/total_short_per_sector.js +1 -1
- package/calculations/short_and_long_stats/long_position_per_stock.js +1 -1
- package/calculations/short_and_long_stats/sentiment_per_stock.js +49 -49
- package/calculations/short_and_long_stats/short_position_per_stock.js +1 -1
- package/calculations/short_and_long_stats/total_long_figures.js +1 -1
- package/calculations/short_and_long_stats/total_short_figures.js +1 -1
- package/calculations/speculators/distance_to_stop_loss_per_leverage.js +78 -78
- package/calculations/speculators/distance_to_tp_per_leverage.js +76 -76
- package/calculations/speculators/entry_distance_to_sl_per_leverage.js +78 -78
- package/calculations/speculators/entry_distance_to_tp_per_leverage.js +77 -77
- package/calculations/speculators/holding_duration_per_asset.js +55 -55
- package/calculations/speculators/leverage_per_asset.js +46 -46
- package/calculations/speculators/leverage_per_sector.js +44 -44
- package/calculations/speculators/risk_reward_ratio_per_asset.js +60 -60
- package/calculations/speculators/speculator_asset_sentiment.js +81 -81
- package/calculations/speculators/speculator_danger_zone.js +57 -57
- package/calculations/speculators/stop_loss_distance_by_sector_short_long_breakdown.js +91 -91
- package/calculations/speculators/stop_loss_distance_by_ticker_short_long_breakdown.js +73 -73
- package/calculations/speculators/stop_loss_per_asset.js +55 -55
- package/calculations/speculators/take_profit_per_asset.js +55 -55
- package/calculations/speculators/tsl_per_asset.js +51 -51
- package/package.json +1 -1
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Counts speculator positions that are close to their stop loss ("Danger Zone").
|
|
3
|
-
* This example defines "close" as within 5% of the current rate.
|
|
4
|
-
*/
|
|
5
|
-
class SpeculatorDangerZone {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.danger_zone = {};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
_initAsset(ticker) {
|
|
11
|
-
if (!this.danger_zone[ticker]) {
|
|
12
|
-
this.danger_zone[ticker] = { long_danger_count: 0, short_danger_count: 0 };
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
process(portfolioData, userId, context) {
|
|
17
|
-
const { instrumentMappings } = context;
|
|
18
|
-
|
|
19
|
-
// FIX: Use the correct PublicPositions property for speculators
|
|
20
|
-
const positions = portfolioData.PublicPositions;
|
|
21
|
-
if (!positions || !Array.isArray(positions)) return;
|
|
22
|
-
|
|
23
|
-
for (const position of positions) {
|
|
24
|
-
// Ensure position has a valid stop loss and rate
|
|
25
|
-
if (!position.StopLossRate || position.StopLossRate <= 0 || !position.CurrentRate) {
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// FIX: Use the correct PascalCase InstrumentID
|
|
30
|
-
const ticker = instrumentMappings[position.InstrumentID];
|
|
31
|
-
if (!ticker) continue;
|
|
32
|
-
|
|
33
|
-
this._initAsset(ticker);
|
|
34
|
-
|
|
35
|
-
let distance_percent = 0;
|
|
36
|
-
// FIX: Use the correct PascalCase IsBuy
|
|
37
|
-
if (position.IsBuy) {
|
|
38
|
-
// Long position: Danger if SL is just below current rate
|
|
39
|
-
distance_percent = (position.CurrentRate - position.StopLossRate) / position.CurrentRate;
|
|
40
|
-
if (distance_percent > 0 && distance_percent < 0.05) {
|
|
41
|
-
this.danger_zone[ticker].long_danger_count++;
|
|
42
|
-
}
|
|
43
|
-
} else {
|
|
44
|
-
// Short position: Danger if SL is just above current rate
|
|
45
|
-
distance_percent = (position.StopLossRate - position.CurrentRate) / position.CurrentRate;
|
|
46
|
-
if (distance_percent > 0 && distance_percent < 0.05) {
|
|
47
|
-
this.danger_zone[ticker].short_danger_count++;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
getResult() {
|
|
54
|
-
return this.danger_zone;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Counts speculator positions that are close to their stop loss ("Danger Zone").
|
|
3
|
+
* This example defines "close" as within 5% of the current rate.
|
|
4
|
+
*/
|
|
5
|
+
class SpeculatorDangerZone {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.danger_zone = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
_initAsset(ticker) {
|
|
11
|
+
if (!this.danger_zone[ticker]) {
|
|
12
|
+
this.danger_zone[ticker] = { long_danger_count: 0, short_danger_count: 0 };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
process(portfolioData, yesterdayPortfolio, userId, context) {
|
|
17
|
+
const { instrumentMappings } = context;
|
|
18
|
+
|
|
19
|
+
// FIX: Use the correct PublicPositions property for speculators
|
|
20
|
+
const positions = portfolioData.PublicPositions;
|
|
21
|
+
if (!positions || !Array.isArray(positions)) return;
|
|
22
|
+
|
|
23
|
+
for (const position of positions) {
|
|
24
|
+
// Ensure position has a valid stop loss and rate
|
|
25
|
+
if (!position.StopLossRate || position.StopLossRate <= 0 || !position.CurrentRate) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// FIX: Use the correct PascalCase InstrumentID
|
|
30
|
+
const ticker = instrumentMappings[position.InstrumentID];
|
|
31
|
+
if (!ticker) continue;
|
|
32
|
+
|
|
33
|
+
this._initAsset(ticker);
|
|
34
|
+
|
|
35
|
+
let distance_percent = 0;
|
|
36
|
+
// FIX: Use the correct PascalCase IsBuy
|
|
37
|
+
if (position.IsBuy) {
|
|
38
|
+
// Long position: Danger if SL is just below current rate
|
|
39
|
+
distance_percent = (position.CurrentRate - position.StopLossRate) / position.CurrentRate;
|
|
40
|
+
if (distance_percent > 0 && distance_percent < 0.05) {
|
|
41
|
+
this.danger_zone[ticker].long_danger_count++;
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
// Short position: Danger if SL is just above current rate
|
|
45
|
+
distance_percent = (position.StopLossRate - position.CurrentRate) / position.CurrentRate;
|
|
46
|
+
if (distance_percent > 0 && distance_percent < 0.05) {
|
|
47
|
+
this.danger_zone[ticker].short_danger_count++;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getResult() {
|
|
54
|
+
return this.danger_zone;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
58
|
module.exports = SpeculatorDangerZone;
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculates the average stop loss distance (percent and value)
|
|
3
|
-
* for long and short positions, grouped by SECTOR.
|
|
4
|
-
*/
|
|
5
|
-
const { getInstrumentSectorMap } = require('../../utils/sector_mapping_provider');
|
|
6
|
-
|
|
7
|
-
class StopLossDistanceBySector {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.instrumentData = {};
|
|
10
|
-
this.instrumentToSector = null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
process(portfolioData, userId) {
|
|
14
|
-
if (!portfolioData || !portfolioData.PublicPositions) return;
|
|
15
|
-
|
|
16
|
-
for (const position of portfolioData.PublicPositions) {
|
|
17
|
-
const { InstrumentID, Leverage, StopLossRate, CurrentRate, IsBuy } = position;
|
|
18
|
-
if (Leverage <= 1 || StopLossRate <= 0.0001 || CurrentRate <= 0) continue;
|
|
19
|
-
|
|
20
|
-
const distance_value = IsBuy ? CurrentRate - StopLossRate : StopLossRate - CurrentRate;
|
|
21
|
-
const distance_percent = (distance_value / CurrentRate) * 100;
|
|
22
|
-
|
|
23
|
-
if (distance_percent > 0) {
|
|
24
|
-
const posType = IsBuy ? 'long' : 'short';
|
|
25
|
-
if (!this.instrumentData[InstrumentID]) this.instrumentData[InstrumentID] = {};
|
|
26
|
-
if (!this.instrumentData[InstrumentID][posType]) {
|
|
27
|
-
this.instrumentData[InstrumentID][posType] = {
|
|
28
|
-
distance_percent_sum: 0,
|
|
29
|
-
distance_value_sum: 0,
|
|
30
|
-
count: 0
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
const agg = this.instrumentData[InstrumentID][posType];
|
|
34
|
-
agg.distance_percent_sum += distance_percent;
|
|
35
|
-
agg.distance_value_sum += distance_value;
|
|
36
|
-
agg.count++;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async getResult() {
|
|
42
|
-
if (Object.keys(this.instrumentData).length === 0) return {};
|
|
43
|
-
if (!this.instrumentToSector) {
|
|
44
|
-
this.instrumentToSector = await getInstrumentSectorMap();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const sectorData = {};
|
|
48
|
-
for (const instrumentId in this.instrumentData) {
|
|
49
|
-
const sector = this.instrumentToSector[instrumentId] || 'N/A';
|
|
50
|
-
if (!sectorData[sector]) sectorData[sector] = {};
|
|
51
|
-
|
|
52
|
-
for (const posType in this.instrumentData[instrumentId]) {
|
|
53
|
-
if (!sectorData[sector][posType]) {
|
|
54
|
-
sectorData[sector][posType] = {
|
|
55
|
-
distance_percent_sum: 0,
|
|
56
|
-
distance_value_sum: 0,
|
|
57
|
-
count: 0
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
const source = this.instrumentData[instrumentId][posType];
|
|
61
|
-
const target = sectorData[sector][posType];
|
|
62
|
-
target.distance_percent_sum += source.distance_percent_sum;
|
|
63
|
-
target.distance_value_sum += source.distance_value_sum;
|
|
64
|
-
target.count += source.count;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const result = {};
|
|
69
|
-
for (const sector in sectorData) {
|
|
70
|
-
result[sector] = {};
|
|
71
|
-
for (const posType in sectorData[sector]) {
|
|
72
|
-
const data = sectorData[sector][posType];
|
|
73
|
-
// REFACTOR: Perform final calculation and return in standardized format.
|
|
74
|
-
if (data.count > 0) {
|
|
75
|
-
result[sector][posType] = {
|
|
76
|
-
average_distance_percent: data.distance_percent_sum / data.count,
|
|
77
|
-
average_distance_value: data.distance_value_sum / data.count,
|
|
78
|
-
count: data.count
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return result;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
reset() {
|
|
87
|
-
this.instrumentData = {};
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
module.exports = StopLossDistanceBySector;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Calculates the average stop loss distance (percent and value)
|
|
3
|
+
* for long and short positions, grouped by SECTOR.
|
|
4
|
+
*/
|
|
5
|
+
const { getInstrumentSectorMap } = require('../../utils/sector_mapping_provider');
|
|
6
|
+
|
|
7
|
+
class StopLossDistanceBySector {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.instrumentData = {};
|
|
10
|
+
this.instrumentToSector = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
process(portfolioData, yesterdayPortfolio, userId, context) {
|
|
14
|
+
if (!portfolioData || !portfolioData.PublicPositions) return;
|
|
15
|
+
|
|
16
|
+
for (const position of portfolioData.PublicPositions) {
|
|
17
|
+
const { InstrumentID, Leverage, StopLossRate, CurrentRate, IsBuy } = position;
|
|
18
|
+
if (Leverage <= 1 || StopLossRate <= 0.0001 || CurrentRate <= 0) continue;
|
|
19
|
+
|
|
20
|
+
const distance_value = IsBuy ? CurrentRate - StopLossRate : StopLossRate - CurrentRate;
|
|
21
|
+
const distance_percent = (distance_value / CurrentRate) * 100;
|
|
22
|
+
|
|
23
|
+
if (distance_percent > 0) {
|
|
24
|
+
const posType = IsBuy ? 'long' : 'short';
|
|
25
|
+
if (!this.instrumentData[InstrumentID]) this.instrumentData[InstrumentID] = {};
|
|
26
|
+
if (!this.instrumentData[InstrumentID][posType]) {
|
|
27
|
+
this.instrumentData[InstrumentID][posType] = {
|
|
28
|
+
distance_percent_sum: 0,
|
|
29
|
+
distance_value_sum: 0,
|
|
30
|
+
count: 0
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const agg = this.instrumentData[InstrumentID][posType];
|
|
34
|
+
agg.distance_percent_sum += distance_percent;
|
|
35
|
+
agg.distance_value_sum += distance_value;
|
|
36
|
+
agg.count++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getResult() {
|
|
42
|
+
if (Object.keys(this.instrumentData).length === 0) return {};
|
|
43
|
+
if (!this.instrumentToSector) {
|
|
44
|
+
this.instrumentToSector = await getInstrumentSectorMap();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const sectorData = {};
|
|
48
|
+
for (const instrumentId in this.instrumentData) {
|
|
49
|
+
const sector = this.instrumentToSector[instrumentId] || 'N/A';
|
|
50
|
+
if (!sectorData[sector]) sectorData[sector] = {};
|
|
51
|
+
|
|
52
|
+
for (const posType in this.instrumentData[instrumentId]) {
|
|
53
|
+
if (!sectorData[sector][posType]) {
|
|
54
|
+
sectorData[sector][posType] = {
|
|
55
|
+
distance_percent_sum: 0,
|
|
56
|
+
distance_value_sum: 0,
|
|
57
|
+
count: 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const source = this.instrumentData[instrumentId][posType];
|
|
61
|
+
const target = sectorData[sector][posType];
|
|
62
|
+
target.distance_percent_sum += source.distance_percent_sum;
|
|
63
|
+
target.distance_value_sum += source.distance_value_sum;
|
|
64
|
+
target.count += source.count;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const result = {};
|
|
69
|
+
for (const sector in sectorData) {
|
|
70
|
+
result[sector] = {};
|
|
71
|
+
for (const posType in sectorData[sector]) {
|
|
72
|
+
const data = sectorData[sector][posType];
|
|
73
|
+
// REFACTOR: Perform final calculation and return in standardized format.
|
|
74
|
+
if (data.count > 0) {
|
|
75
|
+
result[sector][posType] = {
|
|
76
|
+
average_distance_percent: data.distance_percent_sum / data.count,
|
|
77
|
+
average_distance_value: data.distance_value_sum / data.count,
|
|
78
|
+
count: data.count
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
reset() {
|
|
87
|
+
this.instrumentData = {};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = StopLossDistanceBySector;
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculates the average stop loss distance (percent and value)
|
|
3
|
-
* for long and short positions, grouped by TICKER.
|
|
4
|
-
*/
|
|
5
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
6
|
-
|
|
7
|
-
class StopLossDistanceByTicker {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.instrumentData = {};
|
|
10
|
-
this.instrumentToTicker = null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
process(portfolioData, userId) {
|
|
14
|
-
if (!portfolioData || !portfolioData.PublicPositions) return;
|
|
15
|
-
|
|
16
|
-
for (const position of portfolioData.PublicPositions) {
|
|
17
|
-
const { InstrumentID, Leverage, StopLossRate, CurrentRate, IsBuy } = position;
|
|
18
|
-
if (Leverage <= 1 || StopLossRate <= 0.0001 || CurrentRate <= 0) continue;
|
|
19
|
-
|
|
20
|
-
const distance_value = IsBuy ? CurrentRate - StopLossRate : StopLossRate - CurrentRate;
|
|
21
|
-
const distance_percent = (distance_value / CurrentRate) * 100;
|
|
22
|
-
|
|
23
|
-
if (distance_percent > 0) {
|
|
24
|
-
const posType = IsBuy ? 'long' : 'short';
|
|
25
|
-
if (!this.instrumentData[InstrumentID]) this.instrumentData[InstrumentID] = {};
|
|
26
|
-
if (!this.instrumentData[InstrumentID][posType]) {
|
|
27
|
-
this.instrumentData[InstrumentID][posType] = {
|
|
28
|
-
distance_percent_sum: 0,
|
|
29
|
-
distance_value_sum: 0,
|
|
30
|
-
count: 0
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
const agg = this.instrumentData[InstrumentID][posType];
|
|
34
|
-
agg.distance_percent_sum += distance_percent;
|
|
35
|
-
agg.distance_value_sum += distance_value;
|
|
36
|
-
agg.count++;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async getResult() {
|
|
42
|
-
if (Object.keys(this.instrumentData).length === 0) return {};
|
|
43
|
-
if (!this.instrumentToTicker) {
|
|
44
|
-
const mappings = await loadInstrumentMappings();
|
|
45
|
-
this.instrumentToTicker = mappings.instrumentToTicker;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const result = {};
|
|
49
|
-
for (const instrumentId in this.instrumentData) {
|
|
50
|
-
const ticker = this.instrumentToTicker[instrumentId] || `unknown_${instrumentId}`;
|
|
51
|
-
if (!result[ticker]) result[ticker] = {};
|
|
52
|
-
|
|
53
|
-
for (const posType in this.instrumentData[instrumentId]) {
|
|
54
|
-
const data = this.instrumentData[instrumentId][posType];
|
|
55
|
-
// REFACTOR: Perform final calculation and return in standardized format.
|
|
56
|
-
if (data.count > 0) {
|
|
57
|
-
result[ticker][posType] = {
|
|
58
|
-
average_distance_percent: data.distance_percent_sum / data.count,
|
|
59
|
-
average_distance_value: data.distance_value_sum / data.count,
|
|
60
|
-
count: data.count
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return result;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
reset() {
|
|
69
|
-
this.instrumentData = {};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
module.exports = StopLossDistanceByTicker;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Calculates the average stop loss distance (percent and value)
|
|
3
|
+
* for long and short positions, grouped by TICKER.
|
|
4
|
+
*/
|
|
5
|
+
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
6
|
+
|
|
7
|
+
class StopLossDistanceByTicker {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.instrumentData = {};
|
|
10
|
+
this.instrumentToTicker = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
process(portfolioData, yesterdayPortfolio, userId, context) {
|
|
14
|
+
if (!portfolioData || !portfolioData.PublicPositions) return;
|
|
15
|
+
|
|
16
|
+
for (const position of portfolioData.PublicPositions) {
|
|
17
|
+
const { InstrumentID, Leverage, StopLossRate, CurrentRate, IsBuy } = position;
|
|
18
|
+
if (Leverage <= 1 || StopLossRate <= 0.0001 || CurrentRate <= 0) continue;
|
|
19
|
+
|
|
20
|
+
const distance_value = IsBuy ? CurrentRate - StopLossRate : StopLossRate - CurrentRate;
|
|
21
|
+
const distance_percent = (distance_value / CurrentRate) * 100;
|
|
22
|
+
|
|
23
|
+
if (distance_percent > 0) {
|
|
24
|
+
const posType = IsBuy ? 'long' : 'short';
|
|
25
|
+
if (!this.instrumentData[InstrumentID]) this.instrumentData[InstrumentID] = {};
|
|
26
|
+
if (!this.instrumentData[InstrumentID][posType]) {
|
|
27
|
+
this.instrumentData[InstrumentID][posType] = {
|
|
28
|
+
distance_percent_sum: 0,
|
|
29
|
+
distance_value_sum: 0,
|
|
30
|
+
count: 0
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const agg = this.instrumentData[InstrumentID][posType];
|
|
34
|
+
agg.distance_percent_sum += distance_percent;
|
|
35
|
+
agg.distance_value_sum += distance_value;
|
|
36
|
+
agg.count++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getResult() {
|
|
42
|
+
if (Object.keys(this.instrumentData).length === 0) return {};
|
|
43
|
+
if (!this.instrumentToTicker) {
|
|
44
|
+
const mappings = await loadInstrumentMappings();
|
|
45
|
+
this.instrumentToTicker = mappings.instrumentToTicker;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const result = {};
|
|
49
|
+
for (const instrumentId in this.instrumentData) {
|
|
50
|
+
const ticker = this.instrumentToTicker[instrumentId] || `unknown_${instrumentId}`;
|
|
51
|
+
if (!result[ticker]) result[ticker] = {};
|
|
52
|
+
|
|
53
|
+
for (const posType in this.instrumentData[instrumentId]) {
|
|
54
|
+
const data = this.instrumentData[instrumentId][posType];
|
|
55
|
+
// REFACTOR: Perform final calculation and return in standardized format.
|
|
56
|
+
if (data.count > 0) {
|
|
57
|
+
result[ticker][posType] = {
|
|
58
|
+
average_distance_percent: data.distance_percent_sum / data.count,
|
|
59
|
+
average_distance_value: data.distance_value_sum / data.count,
|
|
60
|
+
count: data.count
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
reset() {
|
|
69
|
+
this.instrumentData = {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = StopLossDistanceByTicker;
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculates the average stop loss level per asset.
|
|
3
|
-
*/
|
|
4
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
5
|
-
|
|
6
|
-
class StopLossPerAsset {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.stopLossData = {};
|
|
9
|
-
this.mappings = null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
process(portfolioData, userId) {
|
|
13
|
-
if (portfolioData && portfolioData.PublicPositions) {
|
|
14
|
-
for (const position of portfolioData.PublicPositions) {
|
|
15
|
-
const instrumentId = position.InstrumentID;
|
|
16
|
-
const stopLoss = position.StopLossRate;
|
|
17
|
-
|
|
18
|
-
if (stopLoss > 0) {
|
|
19
|
-
if (!this.stopLossData[instrumentId]) {
|
|
20
|
-
this.stopLossData[instrumentId] = { sum: 0, count: 0 };
|
|
21
|
-
}
|
|
22
|
-
this.stopLossData[instrumentId].sum += stopLoss;
|
|
23
|
-
this.stopLossData[instrumentId].count++;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async getResult() {
|
|
30
|
-
if (!this.mappings) {
|
|
31
|
-
this.mappings = await loadInstrumentMappings();
|
|
32
|
-
}
|
|
33
|
-
const result = {};
|
|
34
|
-
for (const instrumentId in this.stopLossData) {
|
|
35
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
36
|
-
const data = this.stopLossData[instrumentId];
|
|
37
|
-
|
|
38
|
-
// REFACTOR: Perform final calculation directly.
|
|
39
|
-
if (data.count > 0) {
|
|
40
|
-
result[ticker] = {
|
|
41
|
-
average_stop_loss: data.sum / data.count
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
reset() {
|
|
50
|
-
this.stopLossData = {};
|
|
51
|
-
this.mappings = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = StopLossPerAsset;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Calculates the average stop loss level per asset.
|
|
3
|
+
*/
|
|
4
|
+
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
5
|
+
|
|
6
|
+
class StopLossPerAsset {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.stopLossData = {};
|
|
9
|
+
this.mappings = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
process(portfolioData, yesterdayPortfolio, userId, context) {
|
|
13
|
+
if (portfolioData && portfolioData.PublicPositions) {
|
|
14
|
+
for (const position of portfolioData.PublicPositions) {
|
|
15
|
+
const instrumentId = position.InstrumentID;
|
|
16
|
+
const stopLoss = position.StopLossRate;
|
|
17
|
+
|
|
18
|
+
if (stopLoss > 0) {
|
|
19
|
+
if (!this.stopLossData[instrumentId]) {
|
|
20
|
+
this.stopLossData[instrumentId] = { sum: 0, count: 0 };
|
|
21
|
+
}
|
|
22
|
+
this.stopLossData[instrumentId].sum += stopLoss;
|
|
23
|
+
this.stopLossData[instrumentId].count++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getResult() {
|
|
30
|
+
if (!this.mappings) {
|
|
31
|
+
this.mappings = await loadInstrumentMappings();
|
|
32
|
+
}
|
|
33
|
+
const result = {};
|
|
34
|
+
for (const instrumentId in this.stopLossData) {
|
|
35
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
36
|
+
const data = this.stopLossData[instrumentId];
|
|
37
|
+
|
|
38
|
+
// REFACTOR: Perform final calculation directly.
|
|
39
|
+
if (data.count > 0) {
|
|
40
|
+
result[ticker] = {
|
|
41
|
+
average_stop_loss: data.sum / data.count
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reset() {
|
|
50
|
+
this.stopLossData = {};
|
|
51
|
+
this.mappings = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = StopLossPerAsset;
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculates the average take profit level per asset.
|
|
3
|
-
*/
|
|
4
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
5
|
-
|
|
6
|
-
class TakeProfitPerAsset {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.takeProfitData = {};
|
|
9
|
-
this.mappings = null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
process(portfolioData, userId) {
|
|
13
|
-
if (portfolioData && portfolioData.PublicPositions) {
|
|
14
|
-
for (const position of portfolioData.PublicPositions) {
|
|
15
|
-
const instrumentId = position.InstrumentID;
|
|
16
|
-
const takeProfit = position.TakeProfitRate;
|
|
17
|
-
|
|
18
|
-
if (takeProfit > 0) {
|
|
19
|
-
if (!this.takeProfitData[instrumentId]) {
|
|
20
|
-
this.takeProfitData[instrumentId] = { sum: 0, count: 0 };
|
|
21
|
-
}
|
|
22
|
-
this.takeProfitData[instrumentId].sum += takeProfit;
|
|
23
|
-
this.takeProfitData[instrumentId].count++;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async getResult() {
|
|
30
|
-
if (!this.mappings) {
|
|
31
|
-
this.mappings = await loadInstrumentMappings();
|
|
32
|
-
}
|
|
33
|
-
const result = {};
|
|
34
|
-
for (const instrumentId in this.takeProfitData) {
|
|
35
|
-
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
36
|
-
const data = this.takeProfitData[instrumentId];
|
|
37
|
-
|
|
38
|
-
// REFACTOR: Perform final calculation directly.
|
|
39
|
-
if (data.count > 0) {
|
|
40
|
-
result[ticker] = {
|
|
41
|
-
average_take_profit: data.sum / data.count
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
reset() {
|
|
50
|
-
this.takeProfitData = {};
|
|
51
|
-
this.mappings = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = TakeProfitPerAsset;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Calculates the average take profit level per asset.
|
|
3
|
+
*/
|
|
4
|
+
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
5
|
+
|
|
6
|
+
class TakeProfitPerAsset {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.takeProfitData = {};
|
|
9
|
+
this.mappings = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
process(portfolioData, yesterdayPortfolio, userId, context) {
|
|
13
|
+
if (portfolioData && portfolioData.PublicPositions) {
|
|
14
|
+
for (const position of portfolioData.PublicPositions) {
|
|
15
|
+
const instrumentId = position.InstrumentID;
|
|
16
|
+
const takeProfit = position.TakeProfitRate;
|
|
17
|
+
|
|
18
|
+
if (takeProfit > 0) {
|
|
19
|
+
if (!this.takeProfitData[instrumentId]) {
|
|
20
|
+
this.takeProfitData[instrumentId] = { sum: 0, count: 0 };
|
|
21
|
+
}
|
|
22
|
+
this.takeProfitData[instrumentId].sum += takeProfit;
|
|
23
|
+
this.takeProfitData[instrumentId].count++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getResult() {
|
|
30
|
+
if (!this.mappings) {
|
|
31
|
+
this.mappings = await loadInstrumentMappings();
|
|
32
|
+
}
|
|
33
|
+
const result = {};
|
|
34
|
+
for (const instrumentId in this.takeProfitData) {
|
|
35
|
+
const ticker = this.mappings.instrumentToTicker[instrumentId] || instrumentId.toString();
|
|
36
|
+
const data = this.takeProfitData[instrumentId];
|
|
37
|
+
|
|
38
|
+
// REFACTOR: Perform final calculation directly.
|
|
39
|
+
if (data.count > 0) {
|
|
40
|
+
result[ticker] = {
|
|
41
|
+
average_take_profit: data.sum / data.count
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reset() {
|
|
50
|
+
this.takeProfitData = {};
|
|
51
|
+
this.mappings = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = TakeProfitPerAsset;
|