hedgequantx 2.4.34 → 2.4.35
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/package.json +1 -1
- package/src/pages/stats.js +74 -30
package/package.json
CHANGED
package/src/pages/stats.js
CHANGED
|
@@ -187,7 +187,8 @@ const showStats = async (service) => {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// ========== AGGREGATE STATS FROM TRADE HISTORY (API DATA) ==========
|
|
190
|
-
// Calculate stats from
|
|
190
|
+
// Calculate stats from COMPLETED trades only (those with P&L != 0)
|
|
191
|
+
// This matches what we display in TRADES HISTORY
|
|
191
192
|
|
|
192
193
|
let stats = {
|
|
193
194
|
totalTrades: 0, winningTrades: 0, losingTrades: 0,
|
|
@@ -197,24 +198,38 @@ const showStats = async (service) => {
|
|
|
197
198
|
longTrades: 0, shortTrades: 0, longWins: 0, shortWins: 0
|
|
198
199
|
};
|
|
199
200
|
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
// Filter to completed trades only (P&L != 0, not null)
|
|
202
|
+
const completedTrades = allTrades.filter(t => {
|
|
203
|
+
const pnl = t.profitAndLoss || t.pnl;
|
|
204
|
+
return pnl !== null && pnl !== undefined && pnl !== 0;
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Calculate stats from completed trades only
|
|
208
|
+
if (completedTrades.length > 0) {
|
|
209
|
+
stats.totalTrades = completedTrades.length;
|
|
203
210
|
let consecutiveWins = 0, consecutiveLosses = 0;
|
|
204
211
|
|
|
205
|
-
|
|
206
|
-
|
|
212
|
+
// Sort by time for consecutive win/loss calculation
|
|
213
|
+
const sortedTrades = [...completedTrades].sort((a, b) => {
|
|
214
|
+
const timeA = new Date(a.creationTimestamp || a.timestamp || 0).getTime();
|
|
215
|
+
const timeB = new Date(b.creationTimestamp || b.timestamp || 0).getTime();
|
|
216
|
+
return timeA - timeB;
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
for (const trade of sortedTrades) {
|
|
207
220
|
const pnl = trade.profitAndLoss || trade.pnl || 0;
|
|
208
221
|
const size = trade.size || trade.quantity || 1;
|
|
209
|
-
const
|
|
222
|
+
const exitSide = trade.side; // 0=BUY exit (was SHORT), 1=SELL exit (was LONG)
|
|
210
223
|
|
|
211
224
|
stats.totalVolume += Math.abs(size);
|
|
212
225
|
|
|
213
|
-
//
|
|
214
|
-
|
|
226
|
+
// Determine original trade direction from exit side
|
|
227
|
+
// Exit side 0 = BUY to close = was SHORT
|
|
228
|
+
// Exit side 1 = SELL to close = was LONG
|
|
229
|
+
if (exitSide === 1) {
|
|
215
230
|
stats.longTrades++;
|
|
216
231
|
if (pnl > 0) stats.longWins++;
|
|
217
|
-
} else if (
|
|
232
|
+
} else if (exitSide === 0) {
|
|
218
233
|
stats.shortTrades++;
|
|
219
234
|
if (pnl > 0) stats.shortWins++;
|
|
220
235
|
}
|
|
@@ -234,7 +249,6 @@ const showStats = async (service) => {
|
|
|
234
249
|
if (consecutiveLosses > stats.maxConsecutiveLosses) stats.maxConsecutiveLosses = consecutiveLosses;
|
|
235
250
|
if (pnl < stats.worstTrade) stats.worstTrade = pnl;
|
|
236
251
|
}
|
|
237
|
-
// pnl === 0 trades are neither win nor loss
|
|
238
252
|
}
|
|
239
253
|
}
|
|
240
254
|
|
|
@@ -257,8 +271,8 @@ const showStats = async (service) => {
|
|
|
257
271
|
const longWinRate = stats.longTrades > 0 ? ((stats.longWins / stats.longTrades) * 100).toFixed(1) : 'N/A';
|
|
258
272
|
const shortWinRate = stats.shortTrades > 0 ? ((stats.shortWins / stats.shortTrades) * 100).toFixed(1) : 'N/A';
|
|
259
273
|
|
|
260
|
-
// Quantitative metrics (calculated from
|
|
261
|
-
const tradePnLs =
|
|
274
|
+
// Quantitative metrics (calculated from completed trades only)
|
|
275
|
+
const tradePnLs = completedTrades.map(t => t.profitAndLoss || t.pnl || 0);
|
|
262
276
|
const avgReturn = tradePnLs.length > 0 ? tradePnLs.reduce((a, b) => a + b, 0) / tradePnLs.length : 0;
|
|
263
277
|
|
|
264
278
|
// Standard deviation
|
|
@@ -430,20 +444,31 @@ const showStats = async (service) => {
|
|
|
430
444
|
};
|
|
431
445
|
|
|
432
446
|
if (allTrades.length > 0) {
|
|
433
|
-
//
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
const
|
|
437
|
-
const colSymbol = 12;
|
|
438
|
-
const colPrice = 12;
|
|
439
|
-
const colPnl = 12;
|
|
447
|
+
// Column widths - total must equal innerWidth
|
|
448
|
+
// Format: " Time | Symbol | Side | P&L | Fees | Net | Account... "
|
|
449
|
+
const colTime = 9;
|
|
450
|
+
const colSymbol = 10;
|
|
440
451
|
const colSide = 6;
|
|
441
|
-
const
|
|
442
|
-
const
|
|
443
|
-
const
|
|
452
|
+
const colPnl = 10;
|
|
453
|
+
const colFees = 8;
|
|
454
|
+
const colNet = 10;
|
|
455
|
+
// Each column has "| " after it (2 chars), plus leading space (1 char)
|
|
456
|
+
const fixedCols = colTime + colSymbol + colSide + colPnl + colFees + colNet;
|
|
457
|
+
const separatorChars = 6 * 2; // 6 "| " separators
|
|
458
|
+
const leadingSpace = 1;
|
|
459
|
+
const colAccount = innerWidth - fixedCols - separatorChars - leadingSpace;
|
|
444
460
|
|
|
445
|
-
// Header
|
|
446
|
-
const
|
|
461
|
+
// Header - build with exact spacing
|
|
462
|
+
const headerParts = [
|
|
463
|
+
' ' + 'Time'.padEnd(colTime),
|
|
464
|
+
'Symbol'.padEnd(colSymbol),
|
|
465
|
+
'Side'.padEnd(colSide),
|
|
466
|
+
'P&L'.padEnd(colPnl),
|
|
467
|
+
'Fees'.padEnd(colFees),
|
|
468
|
+
'Net'.padEnd(colNet),
|
|
469
|
+
'Account'.padEnd(colAccount)
|
|
470
|
+
];
|
|
471
|
+
const header = headerParts.join('| ');
|
|
447
472
|
console.log(chalk.cyan('\u2551') + chalk.white(header) + chalk.cyan('\u2551'));
|
|
448
473
|
console.log(chalk.cyan('\u255F') + chalk.cyan('\u2500'.repeat(innerWidth)) + chalk.cyan('\u2562'));
|
|
449
474
|
|
|
@@ -464,9 +489,15 @@ const showStats = async (service) => {
|
|
|
464
489
|
const timestamp = trade.creationTimestamp || trade.timestamp;
|
|
465
490
|
const time = timestamp ? new Date(timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true }) : '--:--';
|
|
466
491
|
const symbol = extractSymbol(trade.contractId || trade.symbol);
|
|
467
|
-
const price = (trade.price || 0).toFixed(2);
|
|
468
492
|
const pnl = trade.profitAndLoss || trade.pnl || 0;
|
|
493
|
+
const fees = trade.fees || trade.commission || 0;
|
|
494
|
+
const netPnl = pnl - Math.abs(fees);
|
|
495
|
+
|
|
496
|
+
// Format values
|
|
469
497
|
const pnlText = pnl >= 0 ? `+$${pnl.toFixed(0)}` : `-$${Math.abs(pnl).toFixed(0)}`;
|
|
498
|
+
const feesText = fees !== 0 ? `-$${Math.abs(fees).toFixed(2)}` : '$0';
|
|
499
|
+
const netText = netPnl >= 0 ? `+$${netPnl.toFixed(0)}` : `-$${Math.abs(netPnl).toFixed(0)}`;
|
|
500
|
+
|
|
470
501
|
// For completed trades, show the original direction (opposite of exit side)
|
|
471
502
|
const exitSide = trade.side; // 0=BUY exit means was SHORT, 1=SELL exit means was LONG
|
|
472
503
|
const tradeSide = exitSide === 0 ? 'SHORT' : 'LONG';
|
|
@@ -475,16 +506,29 @@ const showStats = async (service) => {
|
|
|
475
506
|
// Build row with exact widths
|
|
476
507
|
const timeStr = time.padEnd(colTime);
|
|
477
508
|
const symbolStr = symbol.padEnd(colSymbol);
|
|
478
|
-
const priceStr = price.padEnd(colPrice);
|
|
479
|
-
const pnlStr = pnlText.padEnd(colPnl);
|
|
480
509
|
const sideStr = tradeSide.padEnd(colSide);
|
|
481
|
-
const
|
|
510
|
+
const pnlStr = pnlText.padEnd(colPnl);
|
|
511
|
+
const feesStr = feesText.padEnd(colFees);
|
|
512
|
+
const netStr = netText.padEnd(colNet);
|
|
513
|
+
const accountStr = accountName.padEnd(colAccount);
|
|
482
514
|
|
|
483
515
|
// Colored versions
|
|
484
516
|
const pnlColored = pnl >= 0 ? chalk.green(pnlStr) : chalk.red(pnlStr);
|
|
517
|
+
const feesColored = chalk.yellow(feesStr);
|
|
518
|
+
const netColored = netPnl >= 0 ? chalk.green(netStr) : chalk.red(netStr);
|
|
485
519
|
const sideColored = tradeSide === 'LONG' ? chalk.green(sideStr) : chalk.red(sideStr);
|
|
486
520
|
|
|
487
|
-
|
|
521
|
+
// Build row with same format as header
|
|
522
|
+
const rowParts = [
|
|
523
|
+
' ' + timeStr,
|
|
524
|
+
symbolStr,
|
|
525
|
+
sideColored,
|
|
526
|
+
pnlColored,
|
|
527
|
+
feesColored,
|
|
528
|
+
netColored,
|
|
529
|
+
accountStr
|
|
530
|
+
];
|
|
531
|
+
const row = rowParts.join('| ');
|
|
488
532
|
console.log(chalk.cyan('\u2551') + row + chalk.cyan('\u2551'));
|
|
489
533
|
}
|
|
490
534
|
|