hedgequantx 2.9.224 → 2.9.225

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.224",
3
+ "version": "2.9.225",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -59,6 +59,13 @@ function createHandlers(daemon) {
59
59
  name: daemon.rithmic.propfirm.name,
60
60
  };
61
61
 
62
+ // Save credentials for auto-reconnect
63
+ daemon._saveCredentials(username, password);
64
+ daemon._lastRithmicResponse = Date.now();
65
+
66
+ // Start health monitoring
67
+ daemon._startHealthCheck();
68
+
62
69
  // Save session for restore
63
70
  const { storage } = require('../session');
64
71
  storage.save([{
@@ -113,6 +120,14 @@ function createHandlers(daemon) {
113
120
  key: propfirmKey,
114
121
  name: daemon.rithmic.propfirm.name,
115
122
  };
123
+
124
+ // Save credentials for auto-reconnect
125
+ daemon._saveCredentials(credentials.username, credentials.password);
126
+ daemon._lastRithmicResponse = Date.now();
127
+
128
+ // Start health monitoring
129
+ daemon._startHealthCheck();
130
+
116
131
  log.info('Session restored', { propfirm: daemon.propfirm.name });
117
132
  } else {
118
133
  daemon.rithmic = null;
@@ -129,6 +144,10 @@ function createHandlers(daemon) {
129
144
  }
130
145
 
131
146
  async function handleLogout(socket, id) {
147
+ // Stop health monitoring
148
+ daemon._stopHealthCheck();
149
+ daemon._savedCredentials = null;
150
+
132
151
  if (daemon.rithmic) {
133
152
  await daemon.rithmic.disconnect();
134
153
  daemon.rithmic = null;
@@ -56,6 +56,18 @@ class DaemonServer extends EventEmitter {
56
56
 
57
57
  /** @type {Object} Message handlers */
58
58
  this.handlers = createHandlers(this);
59
+
60
+ /** @type {NodeJS.Timer|null} Health check interval */
61
+ this._healthCheckInterval = null;
62
+
63
+ /** @type {number} Last successful Rithmic response timestamp */
64
+ this._lastRithmicResponse = 0;
65
+
66
+ /** @type {boolean} Is currently reconnecting */
67
+ this._isReconnecting = false;
68
+
69
+ /** @type {Object} Saved credentials for reconnection */
70
+ this._savedCredentials = null;
59
71
  }
60
72
 
61
73
  /**
@@ -264,6 +276,138 @@ class DaemonServer extends EventEmitter {
264
276
  */
265
277
  _setupRithmicEvents() {
266
278
  setupRithmicEvents(this);
279
+
280
+ // Monitor disconnection
281
+ if (this.rithmic) {
282
+ this.rithmic.on('disconnected', (info) => {
283
+ log.warn('Rithmic disconnected', info);
284
+ this._handleRithmicDisconnect();
285
+ });
286
+
287
+ this.rithmic.on('reconnected', () => {
288
+ log.info('Rithmic reconnected');
289
+ this._lastRithmicResponse = Date.now();
290
+ this._isReconnecting = false;
291
+ });
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Start health monitoring
297
+ */
298
+ _startHealthCheck() {
299
+ if (this._healthCheckInterval) return;
300
+
301
+ // Check every 30 seconds
302
+ this._healthCheckInterval = setInterval(() => {
303
+ this._performHealthCheck();
304
+ }, 30000);
305
+
306
+ log.debug('Health check started');
307
+ }
308
+
309
+ /**
310
+ * Stop health monitoring
311
+ */
312
+ _stopHealthCheck() {
313
+ if (this._healthCheckInterval) {
314
+ clearInterval(this._healthCheckInterval);
315
+ this._healthCheckInterval = null;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Perform health check on Rithmic connection
321
+ */
322
+ async _performHealthCheck() {
323
+ if (!this.rithmic || this._isReconnecting) return;
324
+
325
+ try {
326
+ // Try to get accounts as a health check (uses cache, no API call)
327
+ const result = await this.rithmic.getTradingAccounts();
328
+
329
+ if (result.success) {
330
+ this._lastRithmicResponse = Date.now();
331
+ } else {
332
+ log.warn('Health check failed', { error: result.error });
333
+
334
+ // If no response for 2 minutes, try reconnect
335
+ const timeSinceLastResponse = Date.now() - this._lastRithmicResponse;
336
+ if (timeSinceLastResponse > 120000) {
337
+ log.warn('Rithmic unresponsive for 2 minutes, attempting reconnect');
338
+ this._handleRithmicDisconnect();
339
+ }
340
+ }
341
+ } catch (err) {
342
+ log.error('Health check error', { error: err.message });
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Handle Rithmic disconnection - attempt reconnect
348
+ */
349
+ async _handleRithmicDisconnect() {
350
+ if (this._isReconnecting) {
351
+ log.debug('Already reconnecting, skipping');
352
+ return;
353
+ }
354
+
355
+ if (!this._savedCredentials) {
356
+ log.warn('Cannot reconnect: no saved credentials');
357
+ return;
358
+ }
359
+
360
+ this._isReconnecting = true;
361
+
362
+ // Notify clients
363
+ this._broadcast(createMessage(MSG_TYPE.EVENT_DISCONNECTED, {
364
+ reason: 'Connection lost, reconnecting...',
365
+ reconnecting: true,
366
+ }));
367
+
368
+ // Use reconnect module
369
+ const { handleAutoReconnect } = require('../rithmic/reconnect');
370
+
371
+ // Prepare service with credentials
372
+ if (this.rithmic) {
373
+ this.rithmic.credentials = this._savedCredentials;
374
+ }
375
+
376
+ try {
377
+ await handleAutoReconnect(this.rithmic);
378
+
379
+ if (this.rithmic && this.rithmic.accounts?.length > 0) {
380
+ this._lastRithmicResponse = Date.now();
381
+ this._isReconnecting = false;
382
+
383
+ // Notify clients of reconnection
384
+ this._broadcast(createMessage(MSG_TYPE.EVENT_RECONNECTED, {
385
+ propfirm: this.propfirm,
386
+ accounts: this.rithmic.accounts.length,
387
+ }));
388
+
389
+ log.info('Reconnection successful');
390
+ }
391
+ } catch (err) {
392
+ log.error('Reconnection failed', { error: err.message });
393
+ this._isReconnecting = false;
394
+
395
+ // Schedule retry in 5 minutes
396
+ setTimeout(() => {
397
+ if (this.isRunning && !this._isReconnecting) {
398
+ this._handleRithmicDisconnect();
399
+ }
400
+ }, 300000);
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Save credentials for reconnection
406
+ * @param {string} username
407
+ * @param {string} password
408
+ */
409
+ _saveCredentials(username, password) {
410
+ this._savedCredentials = { username, password };
267
411
  }
268
412
 
269
413
  /**
@@ -296,6 +440,9 @@ class DaemonServer extends EventEmitter {
296
440
  async stop() {
297
441
  log.info('Stopping daemon...');
298
442
 
443
+ // Stop health monitoring
444
+ this._stopHealthCheck();
445
+
299
446
  // Stop all algo sessions
300
447
  for (const [id, session] of this.algoSessions) {
301
448
  try {