hedgequantx 1.2.58 → 1.2.60

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.58",
3
+ "version": "1.2.60",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
package/src/pages/algo.js CHANGED
@@ -331,8 +331,30 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
331
331
  console.clear();
332
332
  };
333
333
 
334
+ // Check market hours
335
+ const checkMarketStatus = () => {
336
+ const now = new Date();
337
+ const utcDay = now.getUTCDay();
338
+ const utcHour = now.getUTCHours();
339
+ const isDST = (() => {
340
+ const jan = new Date(now.getFullYear(), 0, 1);
341
+ const jul = new Date(now.getFullYear(), 6, 1);
342
+ return now.getTimezoneOffset() < Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
343
+ })();
344
+ const ctOffset = isDST ? 5 : 6;
345
+ const ctHour = (utcHour - ctOffset + 24) % 24;
346
+ const ctDay = utcHour < ctOffset ? (utcDay + 6) % 7 : utcDay;
347
+
348
+ if (ctDay === 6) return { isOpen: false, message: 'Market closed (Saturday)' };
349
+ if (ctDay === 0 && ctHour < 17) return { isOpen: false, message: 'Market opens Sunday 5:00 PM CT' };
350
+ if (ctDay === 5 && ctHour >= 16) return { isOpen: false, message: 'Market closed (Friday after 4PM CT)' };
351
+ if (ctHour === 16 && ctDay >= 1 && ctDay <= 4) return { isOpen: false, message: 'Daily maintenance (4:00-5:00 PM CT)' };
352
+ return { isOpen: true, message: 'Market OPEN' };
353
+ };
354
+
334
355
  const displayUI = () => {
335
356
  clearScreen();
357
+ const marketStatus = checkMarketStatus();
336
358
  console.log();
337
359
  console.log(chalk.gray(getSeparator()));
338
360
  console.log(chalk.cyan.bold(' HQX Ultra-Scalping Algo'));
@@ -341,7 +363,8 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
341
363
  console.log(chalk.white(` Account: ${chalk.cyan(accountName)}`));
342
364
  console.log(chalk.white(` Symbol: ${chalk.cyan(symbolName)}`));
343
365
  console.log(chalk.white(` Contracts: ${chalk.cyan(numContracts)}`));
344
- console.log(chalk.white(` Mode: ${hqxConnected ? chalk.green('LIVE') : chalk.yellow('OFFLINE')}`));
366
+ console.log(chalk.white(` Server: ${hqxConnected ? chalk.green('CONNECTED') : chalk.red('DISCONNECTED')}`));
367
+ console.log(chalk.white(` Market: ${marketStatus.isOpen ? chalk.green(marketStatus.message) : chalk.red(marketStatus.message)}`));
345
368
  console.log(chalk.gray(getSeparator()));
346
369
 
347
370
  // Risk Management
@@ -558,9 +581,55 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
558
581
  // Stop algo
559
582
  if (!stopReason) {
560
583
  addLog('warning', 'Stopping algo...');
561
- displayUI();
562
584
  }
563
585
 
586
+ // Cancel all pending orders and close positions
587
+ addLog('info', 'Cancelling pending orders...');
588
+ displayUI();
589
+
590
+ try {
591
+ // Cancel all orders
592
+ const cancelResult = await service.cancelAllOrders(account.accountId);
593
+ if (cancelResult.success) {
594
+ addLog('success', 'All pending orders cancelled');
595
+ } else {
596
+ addLog('warning', 'No pending orders to cancel');
597
+ }
598
+ } catch (e) {
599
+ addLog('warning', 'Could not cancel orders: ' + e.message);
600
+ }
601
+
602
+ displayUI();
603
+
604
+ // Close all positions for this symbol
605
+ addLog('info', 'Closing open positions...');
606
+ displayUI();
607
+
608
+ try {
609
+ const positions = await service.getPositions(account.accountId);
610
+ if (positions.success && positions.positions) {
611
+ const symbolPos = positions.positions.find(p =>
612
+ p.symbol === symbol ||
613
+ p.contractId === (contract.id || contract.contractId)
614
+ );
615
+
616
+ if (symbolPos && symbolPos.quantity !== 0) {
617
+ const closeResult = await service.closePosition(account.accountId, symbolPos.contractId || symbolPos.symbol);
618
+ if (closeResult.success) {
619
+ addLog('success', `Position closed: ${Math.abs(symbolPos.quantity)} ${symbol}`);
620
+ } else {
621
+ addLog('error', 'Failed to close position: ' + (closeResult.error || 'Unknown'));
622
+ }
623
+ } else {
624
+ addLog('info', 'No open position to close');
625
+ }
626
+ }
627
+ } catch (e) {
628
+ addLog('warning', 'Could not close positions: ' + e.message);
629
+ }
630
+
631
+ displayUI();
632
+
564
633
  if (hqxConnected && algoRunning) {
565
634
  hqxServer.stopAlgo();
566
635
  }
@@ -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