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
|
@@ -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 {
|