aiden-shared-calculations-unified 1.0.95 → 1.0.96

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.
Files changed (54) hide show
  1. package/calculations/capitulation/asset-volatility-estimator.js +1 -2
  2. package/calculations/capitulation/retail-capitulation-risk-forecast.js +1 -2
  3. package/calculations/ghost-book/cost-basis-density.js +1 -2
  4. package/calculations/ghost-book/liquidity-vacuum.js +1 -2
  5. package/calculations/ghost-book/retail-gamma-exposure.js +0 -1
  6. package/calculations/predicative-alpha/cognitive-dissonance.js +1 -2
  7. package/calculations/predicative-alpha/diamond-hand-fracture.js +1 -2
  8. package/calculations/predicative-alpha/mimetic-latency.js +1 -2
  9. package/package.json +1 -1
  10. package/calculations/legacy/activity_by_pnl_status.js +0 -119
  11. package/calculations/legacy/asset_crowd_flow.js +0 -163
  12. package/calculations/legacy/capital_deployment_strategy.js +0 -108
  13. package/calculations/legacy/capital_liquidation_performance.js +0 -139
  14. package/calculations/legacy/capital_vintage_performance.js +0 -136
  15. package/calculations/legacy/cash-flow-deployment.js +0 -144
  16. package/calculations/legacy/cash-flow-liquidation.js +0 -146
  17. package/calculations/legacy/crowd-cash-flow-proxy.js +0 -128
  18. package/calculations/legacy/crowd_conviction_score.js +0 -261
  19. package/calculations/legacy/crowd_sharpe_ratio_proxy.js +0 -137
  20. package/calculations/legacy/daily_asset_activity.js +0 -128
  21. package/calculations/legacy/daily_user_activity_tracker.js +0 -182
  22. package/calculations/legacy/deposit_withdrawal_percentage.js +0 -125
  23. package/calculations/legacy/diversification_pnl.js +0 -115
  24. package/calculations/legacy/drawdown_response.js +0 -137
  25. package/calculations/legacy/dumb-cohort-flow.js +0 -238
  26. package/calculations/legacy/gain_response.js +0 -137
  27. package/calculations/legacy/historical_performance_aggregator.js +0 -85
  28. package/calculations/legacy/in_loss_asset_crowd_flow.js +0 -168
  29. package/calculations/legacy/in_profit_asset_crowd_flow.js +0 -168
  30. package/calculations/legacy/negative_expectancy_cohort_flow.js +0 -232
  31. package/calculations/legacy/new_allocation_percentage.js +0 -98
  32. package/calculations/legacy/paper_vs_diamond_hands.js +0 -107
  33. package/calculations/legacy/position_count_pnl.js +0 -120
  34. package/calculations/legacy/positive_expectancy_cohort_flow.js +0 -232
  35. package/calculations/legacy/profit_cohort_divergence.js +0 -115
  36. package/calculations/legacy/profitability_migration.js +0 -104
  37. package/calculations/legacy/reallocation_increase_percentage.js +0 -104
  38. package/calculations/legacy/risk_appetite_change.js +0 -97
  39. package/calculations/legacy/sector_rotation.js +0 -117
  40. package/calculations/legacy/shark_attack_signal.js +0 -112
  41. package/calculations/legacy/smart-cohort-flow.js +0 -238
  42. package/calculations/legacy/smart-dumb-divergence-index.js +0 -143
  43. package/calculations/legacy/smart_dumb_divergence_index_v2.js +0 -138
  44. package/calculations/legacy/smart_money_flow.js +0 -198
  45. package/calculations/legacy/social-predictive-regime-state.js +0 -102
  46. package/calculations/legacy/social-topic-driver-index.js +0 -147
  47. package/calculations/legacy/social-topic-predictive-potential.js +0 -461
  48. package/calculations/legacy/social_flow_correlation.js +0 -112
  49. package/calculations/legacy/speculator_adjustment_activity.js +0 -103
  50. package/calculations/legacy/strategy-performance.js +0 -265
  51. package/calculations/legacy/tsl_effectiveness.js +0 -85
  52. package/calculations/legacy/user-investment-profile.js +0 -313
  53. package/calculations/legacy/user_expectancy_score.js +0 -106
  54. package/calculations/legacy/user_profitability_tracker.js +0 -131
@@ -1,112 +0,0 @@
1
- /**
2
- * @fileoverview Meta-calculation (Pass 2) to correlate daily social sentiment with
3
- * the actual crowd asset flow.
4
- *
5
- * --- META REFACTOR (v2) ---
6
- * This calculation is now stateless. It declares its dependencies and
7
- * expects them to be passed to its `process` method.
8
- */
9
-
10
- class SocialFlowCorrelation {
11
-
12
- /**
13
- * (NEW) Statically declare dependencies.
14
- */
15
- static getDependencies() {
16
- return ['social-sentiment-aggregation', 'asset-crowd-flow'];
17
- }
18
-
19
- constructor() {
20
- this.bullishSentimentThreshold = 70.0;
21
- this.bearishSentimentThreshold = 30.0;
22
- this.positiveFlowThreshold = 0.005;
23
- this.negativeFlowThreshold = -0.005;
24
- }
25
-
26
- /**
27
- * REFACTORED PROCESS METHOD
28
- * @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
29
- * @param {object} dependencies The shared dependencies (db, logger).
30
- * @param {object} config The computation system configuration.
31
- * @param {object} fetchedDependencies In-memory results from previous passes.
32
- * e.g., { 'social-sentiment-aggregation': ..., 'asset-crowd-flow': ... }
33
- * @returns {Promise<object|null>} The analysis result or null.
34
- */
35
- async process(dateStr, dependencies, config, fetchedDependencies) {
36
- const { logger } = dependencies;
37
-
38
- // 1. Get dependencies from in-memory cache
39
- const socialData = fetchedDependencies['social-sentiment-aggregation'];
40
- const flowData = fetchedDependencies['asset-crowd-flow'];
41
-
42
- // 2. Handle missing dependencies
43
- if (!socialData || !flowData) {
44
- logger.log('WARN', `[SocialFlowCorrelation] Missing computed dependency data for ${dateStr}. Skipping.`);
45
- return null;
46
- }
47
-
48
- // 3. If data exists, perform the correlation
49
- const sentimentMap = socialData.tickerSentiment || {};
50
- const correlationResults = {};
51
-
52
- // Use all tickers from the flow data as the primary loop
53
- for (const ticker in flowData) {
54
- if (!flowData[ticker] || typeof flowData[ticker].net_crowd_flow_pct === 'undefined') {
55
- continue;
56
- }
57
-
58
- const flow = flowData[ticker].net_crowd_flow_pct;
59
- const sentiment = sentimentMap[ticker]?.sentimentRatio;
60
-
61
- if (typeof sentiment === 'undefined') {
62
- correlationResults[ticker] = {
63
- status: 'no_social_sentiment',
64
- net_crowd_flow_pct: flow
65
- };
66
- continue;
67
- }
68
-
69
- // --- The "Jaw-Drop" Logic ---
70
- if (sentiment >= this.bullishSentimentThreshold && flow <= this.negativeFlowThreshold) {
71
- correlationResults[ticker] = {
72
- status: 'Bullish Divergence',
73
- detail: 'Crowd is publicly bullish but is net-selling the asset.',
74
- sentiment_ratio: sentiment,
75
- net_crowd_flow_pct: flow
76
- };
77
- } else if (sentiment <= this.bearishSentimentThreshold && flow >= this.positiveFlowThreshold) {
78
- correlationResults[ticker] = {
79
- status: 'Bearish Divergence',
80
- detail: 'Crowd is publicly bearish but is net-buying the asset.',
81
- sentiment_ratio: sentiment,
82
- net_crowd_flow_pct: flow
83
- };
84
- } else if (sentiment >= this.bullishSentimentThreshold && flow >= this.positiveFlowThreshold) {
85
- correlationResults[ticker] = {
86
- status: 'High Conviction Buy',
87
- sentiment_ratio: sentiment,
88
- net_crowd_flow_pct: flow
89
- };
90
- } else if (sentiment <= this.bearishSentimentThreshold && flow <= this.negativeFlowThreshold) {
91
- correlationResults[ticker] = {
92
- status: 'High Conviction Sell',
93
- sentiment_ratio: sentiment,
94
- net_crowd_flow_pct: flow
95
- };
96
- } else {
97
- correlationResults[ticker] = {
98
- status: 'No Clear Signal',
99
- sentiment_ratio: sentiment,
100
- net_crowd_flow_pct: flow
101
- };
102
- }
103
- }
104
-
105
- return correlationResults;
106
- }
107
-
108
- async getResult() { return null; }
109
- reset() {}
110
- }
111
-
112
- module.exports = SocialFlowCorrelation;
@@ -1,103 +0,0 @@
1
- /**
2
- * @fileoverview Tracks "tinkering" activity from speculators.
3
- * Instead of just opening/closing, this counts how many users
4
- * actively *adjusted* the SL, TP, or TSL on existing trades.
5
- */
6
- class SpeculatorAdjustmentActivity {
7
- constructor() {
8
- // Use Sets to count unique users
9
- this.sl_adjusted_users = new Set();
10
- this.tp_adjusted_users = new Set();
11
- this.tsl_toggled_users = new Set();
12
- }
13
-
14
- /**
15
- * Defines the output schema for this calculation.
16
- * @returns {object} JSON Schema object
17
- */
18
- static getSchema() {
19
- return {
20
- "type": "object",
21
- "description": "Tracks unique speculators who 'tinkered' with open trades by adjusting SL, TP, or TSL.",
22
- "properties": {
23
- "unique_users_adjusted_sl": {
24
- "type": "number",
25
- "description": "Count of unique speculators who adjusted a Stop Loss on at least one position."
26
- },
27
- "unique_users_adjusted_tp": {
28
- "type": "number",
29
- "description": "Count of unique speculators who adjusted a Take Profit on at least one position."
30
- },
31
- "unique_users_toggled_tsl": {
32
- "type": "number",
33
- "description": "Count of unique speculators who enabled or disabled a Trailing Stop Loss on at least one position."
34
- }
35
- },
36
- "required": ["unique_users_adjusted_sl", "unique_users_adjusted_tp", "unique_users_toggled_tsl"]
37
- };
38
- }
39
-
40
- _getPublicPositionsMap(portfolio) {
41
- const positions = portfolio?.PublicPositions;
42
- if (!positions || !Array.isArray(positions)) {
43
- return new Map();
44
- }
45
- // Map<PositionID, PositionObject>
46
- return new Map(positions.map(p => [p.PositionID, p]));
47
- }
48
-
49
- process(todayPortfolio, yesterdayPortfolio, userId) {
50
- // This calculation is only for speculators
51
- if (todayPortfolio?.context?.userType !== 'speculator' || !yesterdayPortfolio) {
52
- return;
53
- }
54
-
55
- const yPosMap = this._getPublicPositionsMap(yesterdayPortfolio);
56
- const tPosMap = this._getPublicPositionsMap(todayPortfolio);
57
-
58
- if (yPosMap.size === 0 || tPosMap.size === 0) {
59
- return; // No positions to compare
60
- }
61
-
62
- for (const [tPosId, tPos] of tPosMap.entries()) {
63
- // Check if this position existed yesterday
64
- if (yPosMap.has(tPosId)) {
65
- const yPos = yPosMap.get(tPosId);
66
-
67
- // 1. Check for Stop Loss adjustment
68
- if (tPos.StopLossRate !== yPos.StopLossRate) {
69
- this.sl_adjusted_users.add(userId);
70
- }
71
-
72
- // 2. Check for Take Profit adjustment
73
- if (tPos.TakeProfitRate !== yPos.TakeProfitRate) {
74
- this.tp_adjusted_users.add(userId);
75
- }
76
-
77
- // 3. Check if TSL was toggled on or off
78
- if (tPos.IsTslEnabled !== yPos.IsTslEnabled) {
79
- this.tsl_toggled_users.add(userId);
80
- }
81
- }
82
- }
83
- }
84
-
85
- getResult() {
86
- return {
87
- // Count of unique users who adjusted at least one trade's SL
88
- unique_users_adjusted_sl: this.sl_adjusted_users.size,
89
- // Count of unique users who adjusted at least one trade's TP
90
- unique_users_adjusted_tp: this.tp_adjusted_users.size,
91
- // Count of unique users who toggled TSL on or off
92
- unique_users_toggled_tsl: this.tsl_toggled_users.size
93
- };
94
- }
95
-
96
- reset() {
97
- this.sl_adjusted_users.clear();
98
- this.tp_adjusted_users.clear();
99
- this.tsl_toggled_users.clear();
100
- }
101
- }
102
-
103
- module.exports = SpeculatorAdjustmentActivity;
@@ -1,265 +0,0 @@
1
- /**
2
- * @fileoverview Backtest (Pass 5) calculation.
3
- * Runs a full historical simulation of a trading strategy.
4
- *
5
- * --- META REFACTOR (v2) ---
6
- * This calculation ignores the `fetchedDependencies` argument.
7
- * It runs a full historical backtest up to `dateStr` by reading
8
- * signal history directly from Firestore. Its dependencies in the
9
- * manifest are for *scheduling* only (i.e., run this last).
10
- */
11
-
12
- const { loadAllPriceData } = require('../../utils/price_data_provider');
13
- const { FieldPath } = require('@google-cloud/firestore');
14
-
15
- class StrategyPerformance {
16
-
17
- /**
18
- * (NEW) Statically declare dependencies.
19
- * These are for *ordering only* to ensure this runs in Pass 5.
20
- * The `process` method does not use them.
21
- */
22
- static getDependencies() {
23
- return ['smart-dumb-divergence-index', 'profit-cohort-divergence'];
24
- }
25
-
26
- /**
27
- * Defines the output schema for this calculation.
28
- * @returns {object} JSON Schema object
29
- */
30
- static getSchema() {
31
- return {
32
- "type": ["object", "null"],
33
- "description": "Result of a full historical backtest of a trading strategy. Returns null if simulation fails.",
34
- "properties": {
35
- "strategyName": {
36
- "type": "string",
37
- "description": "The name of the simulated strategy."
38
- },
39
- "inceptionDate": {
40
- "type": "string",
41
- "description": "The first date a signal was found, marking the start of the backtest."
42
- },
43
- "endDate": {
44
- "type": "string",
45
- "description": "The final date of the backtest simulation."
46
- },
47
- "finalPortfolioValue": {
48
- "type": "number",
49
- "description": "The final equity value of the portfolio at the end of the simulation."
50
- },
51
- "totalReturnPercent": {
52
- "type": "number",
53
- "description": "The total percentage return of the strategy from inception to end date."
54
- }
55
- },
56
- // "required" is not needed at the top level since the whole object can be null.
57
- // If the object is *not* null, we can imply these fields are required.
58
- "if": {
59
- "type": "object"
60
- },
61
- "then": {
62
- "required": ["strategyName", "inceptionDate", "endDate", "finalPortfolioValue", "totalReturnPercent"]
63
- }
64
- };
65
- }
66
-
67
- constructor() {
68
- this.INITIAL_CASH = 100000;
69
- this.TRADE_SIZE_USD = 5000;
70
- this.strategySignals = {
71
- 'smart-dumb-divergence-index': {
72
- 'Capitulation': 'BUY',
73
- 'Euphoria': 'SELL'
74
- },
75
- 'profit_cohort_divergence': {
76
- 'Capitulation': 'BUY',
77
- 'Profit Taking': 'SELL'
78
- }
79
- };
80
- this.priceMap = null;
81
- }
82
-
83
- async _findSignalInceptionDate(db, collection, computation, category) {
84
- const snapshot = await db.collection(collection)
85
- .where(`${category}.${computation}`, '==', true)
86
- .orderBy(FieldPath.documentId(), 'asc')
87
- .limit(1)
88
- .get();
89
- if (snapshot.empty) return null;
90
- return snapshot.docs[0].id;
91
- }
92
-
93
- async _fetchAllSignals(db, collection, resultsSub, compsSub, dates) {
94
- const refs = [];
95
- const signalMap = new Map();
96
- for (const date of dates) {
97
- for (const computation in this.strategySignals) {
98
- const key = `${date}_${computation}`;
99
- let category = 'meta';
100
- // Determine category based on manifest (or simple rule)
101
- if (computation.includes('cohort') && computation !== 'profit-cohort-divergence') category = 'behavioural';
102
- if (computation === 'profit-cohort-divergence') category = 'meta';
103
-
104
- const docRef = db.collection(collection).doc(date)
105
- .collection(resultsSub).doc(category)
106
- .collection(compsSub).doc(computation);
107
- refs.push({ key, ref: docRef });
108
- }
109
- }
110
- const snapshots = await db.getAll(...refs.map(r => r.ref));
111
- snapshots.forEach((snap, idx) => {
112
- if (snap.exists) signalMap.set(refs[idx].key, snap.data());
113
- });
114
- return signalMap;
115
- }
116
-
117
- _findInstrumentId(ticker) {
118
- // This logic is flawed, as the priceMap doesn't store tickers.
119
- // A proper implementation would use `loadInstrumentMappings`.
120
- // For this refactor, we leave the original logic as-is.
121
- for (const instrumentId in this.priceMap) {
122
- const priceData = this.priceMap[instrumentId];
123
- if (priceData && priceData.ticker && priceData.ticker === ticker) {
124
- return instrumentId;
125
- }
126
- }
127
- // Fallback: assume ticker is ID (will fail for string tickers)
128
- if (this.priceMap[ticker]) return ticker;
129
- return null;
130
- }
131
-
132
-
133
- /**
134
- * REFACTORED PROCESS METHOD
135
- * @param {string} dateStr - Today's date.
136
- * @param {object} dependencies - db, logger.
137
- * @param {object} config - Computation config.
138
- * @param {object} fetchedDependencies - (UNUSED) In-memory results.
139
- */
140
- async process(dateStr, dependencies, config, fetchedDependencies) {
141
- const { db, logger, calculationUtils } = dependencies;
142
- const { resultsCollection, resultsSubcollection, computationsSubcollection } = config;
143
-
144
- // 1. Load Price Data
145
- if (!this.priceMap) {
146
- logger.log('INFO', '[Backtest] Loading all price data for simulation...');
147
- // Use the utility from dependencies
148
- this.priceMap = await calculationUtils.loadAllPriceData();
149
- }
150
-
151
- // 2. Find Backtest Start Date (by finding first-ever signal)
152
- const inceptionDateStr = await this._findSignalInceptionDate(
153
- db,
154
- resultsCollection,
155
- 'smart-dumb-divergence-index',
156
- 'meta' // As defined in manifest
157
- );
158
-
159
- if (!inceptionDateStr) {
160
- logger.log('WARN', '[Backtest] No signal history found. Skipping.');
161
- return null;
162
- }
163
-
164
- // 3. Build Date Range
165
- const allDates = [];
166
- const current = new Date(inceptionDateStr + 'T00:00:00Z');
167
- const end = new Date(dateStr + 'T00:00:00Z');
168
- while (current <= end) {
169
- allDates.push(current.toISOString().slice(0, 10));
170
- current.setUTCDate(current.getUTCDate() + 1);
171
- }
172
-
173
- if (allDates.length < 2) {
174
- logger.log('WARN', '[Backtest] Not enough history to run simulation.');
175
- return null;
176
- }
177
-
178
- // 4. Fetch ALL signals for ALL dates in one go (Must use DB)
179
- logger.log('INFO', `[Backtest] Fetching ${allDates.length} days of signal data...`);
180
- const signalDataMap = await this._fetchAllSignals(
181
- db, resultsCollection, resultsSubcollection, computationsSubcollection, allDates
182
- );
183
-
184
- // 5. --- Run the Simulation Loop ---
185
- const portfolio = { cash: this.INITIAL_CASH, positions: {} };
186
- const history = [];
187
-
188
- for (const date of allDates) {
189
- // A. Mark-to-Market
190
- let portfolioValue = portfolio.cash;
191
- for (const ticker in portfolio.positions) {
192
- const pos = portfolio.positions[ticker];
193
- const price = this.priceMap[pos.instrumentId]?.[date];
194
-
195
- if (price) {
196
- pos.marketValue = price * pos.shares;
197
- portfolioValue += pos.marketValue;
198
- } else {
199
- portfolioValue += pos.marketValue; // Use last known value if price missing
200
- }
201
- }
202
- history.push({ date, portfolioValue });
203
-
204
- // B. Generate trades
205
- const tradesToMake = {};
206
- for (const computation in this.strategySignals) {
207
- const signalData = signalDataMap.get(`${date}_${computation}`);
208
- if (!signalData) continue;
209
-
210
- const signalRules = this.strategySignals[computation];
211
- const assetSignals = signalData.assets || signalData;
212
-
213
- for (const ticker in assetSignals) {
214
- const signal = assetSignals[ticker]?.status;
215
- if (signalRules[signal]) {
216
- tradesToMake[ticker] = signalRules[signal];
217
- }
218
- }
219
- }
220
-
221
- // C. Execute Trades
222
- for (const ticker in tradesToMake) {
223
- const action = tradesToMake[ticker];
224
- // HACK: Use the (flawed) original logic to find ID
225
- const instrumentId = this._findInstrumentId(ticker) || ticker;
226
-
227
- const price = this.priceMap[instrumentId]?.[date];
228
- if (!price || price <= 0) continue;
229
-
230
- if (action === 'BUY' && portfolio.cash >= this.TRADE_SIZE_USD) {
231
- if (!portfolio.positions[ticker]) {
232
- const shares = this.TRADE_SIZE_USD / price;
233
- portfolio.cash -= this.TRADE_SIZE_USD;
234
- portfolio.positions[ticker] = {
235
- shares: shares,
236
- instrumentId: instrumentId,
237
- marketValue: this.TRADE_SIZE_USD
238
- };
239
- }
240
- } else if (action === 'SELL' && portfolio.positions[ticker]) {
241
- portfolio.cash += portfolio.positions[ticker].marketValue;
242
- delete portfolio.positions[ticker];
243
- }
244
- }
245
- } // --- End Simulation Loop ---
246
-
247
- const finalValue = history[history.length - 1]?.portfolioValue || this.INITIAL_CASH;
248
- const totalReturnPct = ((finalValue - this.INITIAL_CASH) / this.INITIAL_CASH) * 100;
249
-
250
- logger.log('INFO', `[Backtest] Simulation complete. Final Value: ${finalValue}, Return: ${totalReturnPct.toFixed(2)}%`);
251
-
252
- return {
253
- strategyName: 'SmartDumbDivergence_v1',
254
- inceptionDate: inceptionDateStr,
255
- endDate: dateStr,
256
- finalPortfolioValue: finalValue,
257
- totalReturnPercent: totalReturnPct,
258
- };
259
- }
260
-
261
- async getResult() { return null; }
262
- reset() {}
263
- }
264
-
265
- module.exports = StrategyPerformance;
@@ -1,85 +0,0 @@
1
- /**
2
- * @fileoverview Calculation (Pass 2) for TSL effectiveness.
3
- *
4
- * This metric answers: "What is the difference in average
5
- * P&L between speculators who use a Trailing Stop Loss (TSL)
6
- * versus those who do not?"
7
- */
8
- class TslEffectiveness {
9
- constructor() {
10
- this.with_tsl = { pnl_sum: 0, count: 0 };
11
- this.without_tsl = { pnl_sum: 0, count: 0 };
12
- }
13
-
14
- /**
15
- * Defines the output schema for this calculation.
16
- * @returns {object} JSON Schema object
17
- */
18
- static getSchema() {
19
- return {
20
- "type": "object",
21
- "description": "Compares the average P&L of speculators who use TSL vs. those who do not.",
22
- "properties": {
23
- "with_tsl_avg_pnl": {
24
- "type": "number",
25
- "description": "Average P&L for positions with TSL enabled."
26
- },
27
- "without_tsl_avg_pnl": {
28
- "type": "number",
29
- "description": "Average P&L for positions without TSL enabled."
30
- },
31
- "effectiveness_delta": {
32
- "type": "number",
33
- "description": "The difference in P&L (With TSL - Without TSL)."
34
- },
35
- "with_tsl_count": { "type": "number" },
36
- "without_tsl_count": { "type": "number" }
37
- },
38
- "required": ["with_tsl_avg_pnl", "without_tsl_avg_pnl", "effectiveness_delta", "with_tsl_count", "without_tsl_count"]
39
- };
40
- }
41
-
42
- process(todayPortfolio, yesterdayPortfolio, userId) {
43
- // This calculation is only for speculators
44
- if (todayPortfolio?.context?.userType !== 'speculator') {
45
- return;
46
- }
47
-
48
- const positions = todayPortfolio.PublicPositions;
49
- if (!positions || !Array.isArray(positions)) {
50
- return;
51
- }
52
-
53
- for (const pos of positions) {
54
- const pnl = pos.NetProfit || 0;
55
-
56
- if (pos.IsTslEnabled) {
57
- this.with_tsl.pnl_sum += pnl;
58
- this.with_tsl.count++;
59
- } else {
60
- this.without_tsl.pnl_sum += pnl;
61
- this.without_tsl.count++;
62
- }
63
- }
64
- }
65
-
66
- getResult() {
67
- const with_avg = (this.with_tsl.count > 0) ? (this.with_tsl.pnl_sum / this.with_tsl.count) : 0;
68
- const without_avg = (this.without_tsl.count > 0) ? (this.without_tsl.pnl_sum / this.without_tsl.count) : 0;
69
-
70
- return {
71
- with_tsl_avg_pnl: with_avg,
72
- without_tsl_avg_pnl: without_avg,
73
- effectiveness_delta: with_avg - without_avg,
74
- with_tsl_count: this.with_tsl.count,
75
- without_tsl_count: this.without_tsl.count
76
- };
77
- }
78
-
79
- reset() {
80
- this.with_tsl = { pnl_sum: 0, count: 0 };
81
- this.without_tsl = { pnl_sum: 0, count: 0 };
82
- }
83
- }
84
-
85
- module.exports = TslEffectiveness;