hedgequantx 2.9.162 → 2.9.163

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": "2.9.162",
3
+ "version": "2.9.163",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -37,15 +37,20 @@ class RithmicBrokerClient extends EventEmitter {
37
37
  // Ensure daemon is running
38
38
  const daemonStatus = await manager.ensureRunning();
39
39
  if (!daemonStatus.success) {
40
- return { success: false, error: daemonStatus.error || 'Failed to start daemon' };
40
+ const errorMsg = daemonStatus.error || 'Failed to start daemon';
41
+ return { success: false, error: `Daemon error: ${errorMsg}` };
41
42
  }
42
43
 
43
44
  return new Promise((resolve) => {
44
- this.ws = new WebSocket(`ws://127.0.0.1:${BROKER_PORT}`);
45
+ try {
46
+ this.ws = new WebSocket(`ws://127.0.0.1:${BROKER_PORT}`);
47
+ } catch (err) {
48
+ return resolve({ success: false, error: `WebSocket create error: ${err.message}` });
49
+ }
45
50
 
46
51
  const timeout = setTimeout(() => {
47
52
  this.ws?.terminate();
48
- resolve({ success: false, error: 'Connection timeout' });
53
+ resolve({ success: false, error: `Connection timeout (port ${BROKER_PORT})` });
49
54
  }, 5000);
50
55
 
51
56
  this.ws.on('open', () => {
@@ -64,7 +69,7 @@ class RithmicBrokerClient extends EventEmitter {
64
69
  this.ws.on('error', (err) => {
65
70
  clearTimeout(timeout);
66
71
  this.connected = false;
67
- resolve({ success: false, error: err.message });
72
+ resolve({ success: false, error: `Daemon connection failed: ${err.message}` });
68
73
  });
69
74
  });
70
75
  }
@@ -67,16 +67,35 @@ class RithmicBrokerDaemon {
67
67
 
68
68
  if (!fs.existsSync(BROKER_DIR)) fs.mkdirSync(BROKER_DIR, { recursive: true });
69
69
  fs.writeFileSync(PID_FILE, String(process.pid));
70
+ log('INFO', 'Starting daemon...', { pid: process.pid });
70
71
 
71
72
  // Restore connections from state (with cached accounts - no API spam)
72
- await this.reconnectManager.restoreConnections(STATE_FILE);
73
+ try {
74
+ await this.reconnectManager.restoreConnections(STATE_FILE);
75
+ } catch (e) {
76
+ log('WARN', 'Failed to restore connections', { error: e.message });
77
+ }
78
+
79
+ // Create WebSocket server with proper error handling
80
+ try {
81
+ this.wss = new WebSocket.Server({ port: BROKER_PORT, host: '127.0.0.1' });
82
+ } catch (e) {
83
+ log('ERROR', 'Failed to create WebSocket server', { error: e.message, port: BROKER_PORT });
84
+ throw e;
85
+ }
86
+
87
+ // Wait for server to be listening
88
+ await new Promise((resolve, reject) => {
89
+ const timeout = setTimeout(() => reject(new Error('WSS listen timeout')), 5000);
90
+ this.wss.on('listening', () => { clearTimeout(timeout); resolve(); });
91
+ this.wss.on('error', (err) => { clearTimeout(timeout); reject(err); });
92
+ });
73
93
 
74
- this.wss = new WebSocket.Server({ port: BROKER_PORT, host: '127.0.0.1' });
75
94
  this.wss.on('connection', (ws) => this._handleClient(ws));
76
95
  this.wss.on('error', (err) => log('ERROR', 'WSS error', { error: err.message }));
77
96
 
78
97
  this.running = true;
79
- log('INFO', 'Daemon started', { pid: process.pid, port: BROKER_PORT });
98
+ log('INFO', 'Daemon started successfully', { pid: process.pid, port: BROKER_PORT });
80
99
 
81
100
  // Save state on ANY termination signal
82
101
  const gracefulShutdown = (signal) => {
@@ -392,8 +411,31 @@ class RithmicBrokerDaemon {
392
411
 
393
412
  // Main entry point
394
413
  if (require.main === module) {
395
- const daemon = new RithmicBrokerDaemon();
396
- daemon.start().catch((e) => { console.error('Daemon failed:', e.message); process.exit(1); });
414
+ // Ensure log directory exists early
415
+ if (!fs.existsSync(BROKER_DIR)) {
416
+ fs.mkdirSync(BROKER_DIR, { recursive: true });
417
+ }
418
+
419
+ // Log startup attempt
420
+ const startupLog = (msg) => {
421
+ const ts = new Date().toISOString();
422
+ fs.appendFileSync(LOG_FILE, `[${ts}] [STARTUP] ${msg}\n`);
423
+ };
424
+
425
+ startupLog(`Daemon starting (pid=${process.pid}, node=${process.version})`);
426
+
427
+ try {
428
+ const daemon = new RithmicBrokerDaemon();
429
+ daemon.start().catch((e) => {
430
+ startupLog(`FATAL: start() failed - ${e.message}`);
431
+ console.error('Daemon failed:', e.message);
432
+ process.exit(1);
433
+ });
434
+ } catch (e) {
435
+ startupLog(`FATAL: constructor failed - ${e.message}`);
436
+ console.error('Daemon failed:', e.message);
437
+ process.exit(1);
438
+ }
397
439
  }
398
440
 
399
441
  module.exports = { RithmicBrokerDaemon, BROKER_PORT, BROKER_DIR, PID_FILE, LOG_FILE, STATE_FILE };
@@ -86,21 +86,29 @@ const start = async () => {
86
86
  child.unref();
87
87
  fs.closeSync(logFd);
88
88
 
89
- // Wait for daemon to start
90
- await new Promise(r => setTimeout(r, 2000));
89
+ // Wait for daemon to start (poll every 500ms, max 5s)
90
+ let attempts = 0;
91
+ const maxAttempts = 10;
92
+ let runStatus = { running: false, pid: null };
91
93
 
92
- const runStatus = await isRunning();
93
- if (runStatus.running) {
94
- return { success: true, error: null, pid: runStatus.pid || child.pid };
95
- } else {
96
- // Read log for error details
97
- let errorDetail = 'Failed to start RithmicBroker daemon';
98
- if (fs.existsSync(LOG_FILE)) {
99
- const log = fs.readFileSync(LOG_FILE, 'utf8').slice(-500);
100
- if (log) errorDetail += `: ${log.split('\n').filter(l => l).pop()}`;
94
+ while (attempts < maxAttempts) {
95
+ await new Promise(r => setTimeout(r, 500));
96
+ runStatus = await isRunning();
97
+ if (runStatus.running) {
98
+ return { success: true, error: null, pid: runStatus.pid || child.pid };
101
99
  }
102
- return { success: false, error: errorDetail, pid: null };
100
+ attempts++;
103
101
  }
102
+
103
+ // Read log for error details
104
+ let errorDetail = 'Daemon failed to start';
105
+ if (fs.existsSync(LOG_FILE)) {
106
+ const logContent = fs.readFileSync(LOG_FILE, 'utf8');
107
+ const lines = logContent.split('\n').filter(l => l.trim());
108
+ const lastLines = lines.slice(-5).join(' | ');
109
+ if (lastLines) errorDetail += ` - Log: ${lastLines}`;
110
+ }
111
+ return { success: false, error: errorDetail, pid: null };
104
112
  } catch (error) {
105
113
  return { success: false, error: error.message, pid: null };
106
114
  }