hedgequantx 1.2.59 → 1.2.62
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/algo.js +93 -88
- package/src/services/projectx.js +37 -0
package/package.json
CHANGED
package/src/pages/algo.js
CHANGED
|
@@ -308,9 +308,6 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
308
308
|
let stopReason = null;
|
|
309
309
|
|
|
310
310
|
// Activity logs
|
|
311
|
-
const logs = [];
|
|
312
|
-
const MAX_LOGS = 10;
|
|
313
|
-
|
|
314
311
|
// Stats
|
|
315
312
|
let stats = {
|
|
316
313
|
trades: 0,
|
|
@@ -321,14 +318,32 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
321
318
|
winRate: '0.0'
|
|
322
319
|
};
|
|
323
320
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
321
|
+
// Log colors
|
|
322
|
+
const typeColors = {
|
|
323
|
+
info: chalk.cyan,
|
|
324
|
+
success: chalk.green,
|
|
325
|
+
signal: chalk.yellow.bold,
|
|
326
|
+
trade: chalk.green.bold,
|
|
327
|
+
error: chalk.red,
|
|
328
|
+
warning: chalk.yellow
|
|
328
329
|
};
|
|
329
330
|
|
|
330
|
-
const
|
|
331
|
-
|
|
331
|
+
const getIcon = (type) => {
|
|
332
|
+
switch(type) {
|
|
333
|
+
case 'signal': return '[*]';
|
|
334
|
+
case 'trade': return '[>]';
|
|
335
|
+
case 'error': return '[X]';
|
|
336
|
+
case 'success': return '[OK]';
|
|
337
|
+
default: return '[.]';
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// Print log directly to console (no clear screen)
|
|
342
|
+
const printLog = (type, message) => {
|
|
343
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
344
|
+
const color = typeColors[type] || chalk.white;
|
|
345
|
+
const icon = getIcon(type);
|
|
346
|
+
console.log(chalk.gray(` [${timestamp}]`) + ' ' + color(`${icon} ${message}`));
|
|
332
347
|
};
|
|
333
348
|
|
|
334
349
|
// Check market hours
|
|
@@ -352,71 +367,22 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
352
367
|
return { isOpen: true, message: 'Market OPEN' };
|
|
353
368
|
};
|
|
354
369
|
|
|
355
|
-
|
|
356
|
-
|
|
370
|
+
// Display header once at start
|
|
371
|
+
const displayHeader = () => {
|
|
372
|
+
console.clear();
|
|
357
373
|
const marketStatus = checkMarketStatus();
|
|
358
374
|
console.log();
|
|
359
375
|
console.log(chalk.gray(getSeparator()));
|
|
360
376
|
console.log(chalk.cyan.bold(' HQX Ultra-Scalping Algo'));
|
|
361
377
|
console.log(chalk.gray(getSeparator()));
|
|
362
|
-
console.log(chalk.white(` Status: ${algoRunning ? chalk.green('RUNNING') : chalk.yellow('CONNECTING...')}`));
|
|
363
378
|
console.log(chalk.white(` Account: ${chalk.cyan(accountName)}`));
|
|
364
379
|
console.log(chalk.white(` Symbol: ${chalk.cyan(symbolName)}`));
|
|
365
380
|
console.log(chalk.white(` Contracts: ${chalk.cyan(numContracts)}`));
|
|
366
|
-
console.log(chalk.white(`
|
|
381
|
+
console.log(chalk.white(` Target: ${chalk.green('$' + dailyTarget.toFixed(2))} | Risk: ${chalk.red('$' + maxRisk.toFixed(2))}`));
|
|
367
382
|
console.log(chalk.white(` Market: ${marketStatus.isOpen ? chalk.green(marketStatus.message) : chalk.red(marketStatus.message)}`));
|
|
368
383
|
console.log(chalk.gray(getSeparator()));
|
|
369
|
-
|
|
370
|
-
// Risk Management
|
|
371
|
-
console.log();
|
|
372
|
-
const targetProgress = Math.min(100, Math.max(0, (stats.pnl / dailyTarget) * 100));
|
|
373
|
-
const riskProgress = Math.min(100, (Math.abs(Math.min(0, stats.pnl)) / maxRisk) * 100);
|
|
374
|
-
console.log(chalk.white(' Target: ') + chalk.green('$' + dailyTarget.toFixed(2)) +
|
|
375
|
-
chalk.gray(' | Progress: ') + (targetProgress >= 100 ? chalk.green.bold(targetProgress.toFixed(1) + '%') : chalk.yellow(targetProgress.toFixed(1) + '%')));
|
|
376
|
-
console.log(chalk.white(' Risk: ') + chalk.red('$' + maxRisk.toFixed(2)) +
|
|
377
|
-
chalk.gray(' | Used: ') + (riskProgress >= 100 ? chalk.red.bold(riskProgress.toFixed(1) + '%') : chalk.cyan(riskProgress.toFixed(1) + '%')));
|
|
378
|
-
|
|
379
|
-
// Stats bar
|
|
380
|
-
console.log();
|
|
381
|
-
console.log(chalk.white(' Stats: ') +
|
|
382
|
-
chalk.gray('Trades: ') + chalk.cyan(stats.trades) +
|
|
383
|
-
chalk.gray(' | Wins: ') + chalk.green(stats.wins) +
|
|
384
|
-
chalk.gray(' | Losses: ') + chalk.red(stats.losses) +
|
|
385
|
-
chalk.gray(' | Win Rate: ') + chalk.yellow(stats.winRate + '%') +
|
|
386
|
-
chalk.gray(' | P&L: ') + (stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2)))
|
|
387
|
-
);
|
|
388
|
-
console.log();
|
|
389
|
-
|
|
390
|
-
// Activity logs
|
|
391
|
-
console.log(chalk.gray(getSeparator()));
|
|
392
|
-
console.log(chalk.white.bold(' Activity Log'));
|
|
393
|
-
console.log(chalk.gray(getSeparator()));
|
|
394
|
-
|
|
395
|
-
const typeColors = {
|
|
396
|
-
info: chalk.cyan,
|
|
397
|
-
success: chalk.green,
|
|
398
|
-
signal: chalk.yellow.bold,
|
|
399
|
-
trade: chalk.green.bold,
|
|
400
|
-
error: chalk.red,
|
|
401
|
-
warning: chalk.yellow
|
|
402
|
-
};
|
|
403
|
-
|
|
404
|
-
if (logs.length === 0) {
|
|
405
|
-
console.log(chalk.gray(' Waiting for activity...'));
|
|
406
|
-
} else {
|
|
407
|
-
logs.forEach(log => {
|
|
408
|
-
const color = typeColors[log.type] || chalk.white;
|
|
409
|
-
const icon = log.type === 'signal' ? '[*]' :
|
|
410
|
-
log.type === 'trade' ? '[>]' :
|
|
411
|
-
log.type === 'error' ? '[X]' :
|
|
412
|
-
log.type === 'success' ? '[OK]' : '[.]';
|
|
413
|
-
console.log(chalk.gray(` [${log.timestamp}]`) + ' ' + color(`${icon} ${log.message}`));
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
console.log(chalk.gray(getSeparator()));
|
|
418
|
-
console.log();
|
|
419
384
|
console.log(chalk.yellow(' Press X to stop algo...'));
|
|
385
|
+
console.log(chalk.gray(getSeparator()));
|
|
420
386
|
console.log();
|
|
421
387
|
};
|
|
422
388
|
|
|
@@ -455,17 +421,15 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
455
421
|
hqxConnected = false;
|
|
456
422
|
}
|
|
457
423
|
|
|
458
|
-
// Setup event handlers
|
|
424
|
+
// Setup event handlers - logs scroll down naturally
|
|
459
425
|
hqxServer.on('log', (data) => {
|
|
460
|
-
|
|
461
|
-
displayUI();
|
|
426
|
+
printLog(data.type || 'info', data.message);
|
|
462
427
|
});
|
|
463
428
|
|
|
464
429
|
hqxServer.on('signal', (data) => {
|
|
465
430
|
stats.signals++;
|
|
466
431
|
const side = data.side === 'long' ? 'BUY' : 'SELL';
|
|
467
|
-
|
|
468
|
-
displayUI();
|
|
432
|
+
printLog('signal', `${side} Signal @ ${data.entry?.toFixed(2) || 'N/A'} | SL: ${data.stop?.toFixed(2) || 'N/A'} | TP: ${data.target?.toFixed(2) || 'N/A'}`);
|
|
469
433
|
|
|
470
434
|
// Execute order via PropFirm API if connected
|
|
471
435
|
if (hqxConnected && service) {
|
|
@@ -478,17 +442,20 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
478
442
|
stats.pnl += data.pnl || 0;
|
|
479
443
|
if (data.pnl > 0) {
|
|
480
444
|
stats.wins++;
|
|
481
|
-
|
|
445
|
+
printLog('trade', `Closed +$${data.pnl.toFixed(2)} (${data.reason || 'take_profit'})`);
|
|
482
446
|
} else {
|
|
483
447
|
stats.losses++;
|
|
484
|
-
|
|
448
|
+
printLog('trade', `Closed -$${Math.abs(data.pnl).toFixed(2)} (${data.reason || 'stop_loss'})`);
|
|
485
449
|
}
|
|
486
450
|
stats.winRate = stats.trades > 0 ? ((stats.wins / stats.trades) * 100).toFixed(1) : '0.0';
|
|
487
451
|
|
|
452
|
+
// Print updated stats
|
|
453
|
+
printLog('info', `Stats: Trades: ${stats.trades} | Wins: ${stats.wins} | P&L: $${stats.pnl.toFixed(2)}`);
|
|
454
|
+
|
|
488
455
|
// Check daily target
|
|
489
456
|
if (stats.pnl >= dailyTarget) {
|
|
490
457
|
stopReason = 'target';
|
|
491
|
-
|
|
458
|
+
printLog('success', `Daily target reached! +$${stats.pnl.toFixed(2)}`);
|
|
492
459
|
algoRunning = false;
|
|
493
460
|
if (hqxConnected) {
|
|
494
461
|
hqxServer.stopAlgo();
|
|
@@ -498,36 +465,35 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
498
465
|
// Check max risk
|
|
499
466
|
if (stats.pnl <= -maxRisk) {
|
|
500
467
|
stopReason = 'risk';
|
|
501
|
-
|
|
468
|
+
printLog('error', `Max risk reached! -$${Math.abs(stats.pnl).toFixed(2)}`);
|
|
502
469
|
algoRunning = false;
|
|
503
470
|
if (hqxConnected) {
|
|
504
471
|
hqxServer.stopAlgo();
|
|
505
472
|
}
|
|
506
473
|
}
|
|
507
|
-
|
|
508
|
-
displayUI();
|
|
509
474
|
});
|
|
510
475
|
|
|
511
476
|
hqxServer.on('stats', (data) => {
|
|
512
477
|
stats = { ...stats, ...data };
|
|
513
|
-
|
|
478
|
+
// Don't print stats every time, only on trades
|
|
514
479
|
});
|
|
515
480
|
|
|
516
481
|
hqxServer.on('error', (data) => {
|
|
517
|
-
|
|
518
|
-
displayUI();
|
|
482
|
+
printLog('error', data.message || 'Unknown error');
|
|
519
483
|
});
|
|
520
484
|
|
|
521
485
|
hqxServer.on('disconnected', () => {
|
|
522
486
|
hqxConnected = false;
|
|
523
|
-
|
|
524
|
-
displayUI();
|
|
487
|
+
printLog('warning', 'Disconnected from HQX Server');
|
|
525
488
|
});
|
|
526
489
|
|
|
490
|
+
// Display header once
|
|
491
|
+
displayHeader();
|
|
492
|
+
|
|
527
493
|
// Start algo
|
|
528
494
|
if (hqxConnected) {
|
|
529
|
-
|
|
530
|
-
|
|
495
|
+
printLog('info', 'Starting HQX Ultra-Scalping...');
|
|
496
|
+
printLog('info', `Target: $${dailyTarget.toFixed(2)} | Risk: $${maxRisk.toFixed(2)}`);
|
|
531
497
|
hqxServer.startAlgo({
|
|
532
498
|
accountId: account.accountId,
|
|
533
499
|
contractId: contract.id || contract.contractId,
|
|
@@ -540,13 +506,11 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
540
506
|
});
|
|
541
507
|
algoRunning = true;
|
|
542
508
|
} else {
|
|
543
|
-
|
|
544
|
-
|
|
509
|
+
printLog('warning', 'Running in offline demo mode');
|
|
510
|
+
printLog('info', 'No real trades will be executed');
|
|
545
511
|
algoRunning = true;
|
|
546
512
|
}
|
|
547
513
|
|
|
548
|
-
displayUI();
|
|
549
|
-
|
|
550
514
|
// Wait for X key OR auto-stop (target/risk reached)
|
|
551
515
|
await new Promise((resolve) => {
|
|
552
516
|
// Check for auto-stop every 500ms
|
|
@@ -579,9 +543,50 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
579
543
|
});
|
|
580
544
|
|
|
581
545
|
// Stop algo
|
|
546
|
+
console.log();
|
|
582
547
|
if (!stopReason) {
|
|
583
|
-
|
|
584
|
-
|
|
548
|
+
printLog('warning', 'Stopping algo...');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Cancel all pending orders and close positions
|
|
552
|
+
printLog('info', 'Cancelling pending orders...');
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
// Cancel all orders
|
|
556
|
+
const cancelResult = await service.cancelAllOrders(account.accountId);
|
|
557
|
+
if (cancelResult.success) {
|
|
558
|
+
printLog('success', 'All pending orders cancelled');
|
|
559
|
+
} else {
|
|
560
|
+
printLog('warning', 'No pending orders to cancel');
|
|
561
|
+
}
|
|
562
|
+
} catch (e) {
|
|
563
|
+
printLog('warning', 'Could not cancel orders: ' + e.message);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Close all positions for this symbol
|
|
567
|
+
printLog('info', 'Closing open positions...');
|
|
568
|
+
|
|
569
|
+
try {
|
|
570
|
+
const positions = await service.getPositions(account.accountId);
|
|
571
|
+
if (positions.success && positions.positions) {
|
|
572
|
+
const symbolPos = positions.positions.find(p =>
|
|
573
|
+
p.symbol === symbol ||
|
|
574
|
+
p.contractId === (contract.id || contract.contractId)
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
if (symbolPos && symbolPos.quantity !== 0) {
|
|
578
|
+
const closeResult = await service.closePosition(account.accountId, symbolPos.contractId || symbolPos.symbol);
|
|
579
|
+
if (closeResult.success) {
|
|
580
|
+
printLog('success', `Position closed: ${Math.abs(symbolPos.quantity)} ${symbol}`);
|
|
581
|
+
} else {
|
|
582
|
+
printLog('error', 'Failed to close position: ' + (closeResult.error || 'Unknown'));
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
printLog('info', 'No open position to close');
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
} catch (e) {
|
|
589
|
+
printLog('warning', 'Could not close positions: ' + e.message);
|
|
585
590
|
}
|
|
586
591
|
|
|
587
592
|
if (hqxConnected && algoRunning) {
|
|
@@ -958,7 +963,7 @@ const launchCopyTrading = async (config) => {
|
|
|
958
963
|
let stopReason = null;
|
|
959
964
|
let lastLeadPosition = null;
|
|
960
965
|
const logs = [];
|
|
961
|
-
const MAX_LOGS =
|
|
966
|
+
const MAX_LOGS = 25;
|
|
962
967
|
|
|
963
968
|
const stats = {
|
|
964
969
|
copiedTrades: 0,
|
package/src/services/projectx.js
CHANGED
|
@@ -311,6 +311,43 @@ class ProjectXService {
|
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Cancels all pending orders for an account
|
|
316
|
+
* @param {number|string} accountId - Account ID
|
|
317
|
+
* @returns {Promise<{success: boolean, cancelled?: number, error?: string}>}
|
|
318
|
+
*/
|
|
319
|
+
async cancelAllOrders(accountId) {
|
|
320
|
+
try {
|
|
321
|
+
const id = validateAccountId(accountId);
|
|
322
|
+
|
|
323
|
+
// First get all pending orders
|
|
324
|
+
const ordersResult = await this.getOrders(id);
|
|
325
|
+
if (!ordersResult.success || !ordersResult.orders) {
|
|
326
|
+
return { success: true, cancelled: 0 };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Filter pending orders (status = working/pending)
|
|
330
|
+
const pendingOrders = ordersResult.orders.filter(o =>
|
|
331
|
+
o.status === 'Working' || o.status === 'Pending' || o.status === 0 || o.status === 1
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
if (pendingOrders.length === 0) {
|
|
335
|
+
return { success: true, cancelled: 0 };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Cancel each order
|
|
339
|
+
let cancelled = 0;
|
|
340
|
+
for (const order of pendingOrders) {
|
|
341
|
+
const result = await this.cancelOrder(order.orderId || order.id);
|
|
342
|
+
if (result.success) cancelled++;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return { success: true, cancelled };
|
|
346
|
+
} catch (error) {
|
|
347
|
+
return { success: false, error: error.message };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
314
351
|
/**
|
|
315
352
|
* Closes a position
|
|
316
353
|
* @param {number|string} accountId - Account ID
|