hedgequantx 2.9.224 → 2.9.226

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.226",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
package/src/app.js CHANGED
@@ -78,10 +78,17 @@ function createDaemonProxyService(client, propfirm, credentials = null) {
78
78
  // Return credentials for algo trading market data connection
79
79
  if (!storedCredentials) return null;
80
80
  const { RITHMIC_ENDPOINTS } = require('./services/rithmic');
81
+ const { getPropFirm } = require('./config/propfirms');
82
+
83
+ // Get the proper rithmicSystem from propfirm config
84
+ const propfirmKey = propfirm?.key || 'apex_rithmic';
85
+ const propfirmConfig = getPropFirm(propfirmKey);
86
+ const systemName = propfirmConfig?.rithmicSystem || propfirm?.rithmicSystem || propfirm?.name || 'Apex';
87
+
81
88
  return {
82
89
  userId: storedCredentials.username,
83
90
  password: storedCredentials.password,
84
- systemName: propfirm?.systemName || propfirm?.name || 'Apex',
91
+ systemName,
85
92
  gateway: RITHMIC_ENDPOINTS?.CHICAGO || 'wss://rprotocol.rithmic.com:443',
86
93
  };
87
94
  },
@@ -54,17 +54,30 @@ function createHandlers(daemon) {
54
54
  const result = await daemon.rithmic.login(username, password);
55
55
 
56
56
  if (result.success) {
57
+ // Get rithmicSystem from config
58
+ const { getPropFirm } = require('../../config/propfirms');
59
+ const propfirmConfig = getPropFirm(propfirmKey);
60
+
57
61
  daemon.propfirm = {
58
62
  key: propfirmKey,
59
63
  name: daemon.rithmic.propfirm.name,
64
+ rithmicSystem: propfirmConfig?.rithmicSystem || daemon.rithmic.propfirm.systemName || 'Apex',
60
65
  };
61
66
 
67
+ // Save credentials for auto-reconnect
68
+ daemon._saveCredentials(username, password);
69
+ daemon._lastRithmicResponse = Date.now();
70
+
71
+ // Start health monitoring
72
+ daemon._startHealthCheck();
73
+
62
74
  // Save session for restore
63
75
  const { storage } = require('../session');
64
76
  storage.save([{
65
77
  type: 'rithmic',
66
78
  propfirm: daemon.propfirm.name,
67
79
  propfirmKey,
80
+ rithmicSystem: daemon.propfirm.rithmicSystem,
68
81
  credentials: { username, password },
69
82
  accounts: daemon.rithmic.accounts,
70
83
  }]);
@@ -109,10 +122,23 @@ function createHandlers(daemon) {
109
122
  );
110
123
 
111
124
  if (result.success) {
125
+ // Get rithmicSystem from config or session
126
+ const { getPropFirm } = require('../../config/propfirms');
127
+ const propfirmConfig = getPropFirm(propfirmKey);
128
+
112
129
  daemon.propfirm = {
113
130
  key: propfirmKey,
114
131
  name: daemon.rithmic.propfirm.name,
132
+ rithmicSystem: propfirmConfig?.rithmicSystem || rithmicSession.rithmicSystem || daemon.rithmic.propfirm.systemName || 'Apex',
115
133
  };
134
+
135
+ // Save credentials for auto-reconnect
136
+ daemon._saveCredentials(credentials.username, credentials.password);
137
+ daemon._lastRithmicResponse = Date.now();
138
+
139
+ // Start health monitoring
140
+ daemon._startHealthCheck();
141
+
116
142
  log.info('Session restored', { propfirm: daemon.propfirm.name });
117
143
  } else {
118
144
  daemon.rithmic = null;
@@ -129,6 +155,10 @@ function createHandlers(daemon) {
129
155
  }
130
156
 
131
157
  async function handleLogout(socket, id) {
158
+ // Stop health monitoring
159
+ daemon._stopHealthCheck();
160
+ daemon._savedCredentials = null;
161
+
132
162
  if (daemon.rithmic) {
133
163
  await daemon.rithmic.disconnect();
134
164
  daemon.rithmic = null;
@@ -280,6 +310,13 @@ function createHandlers(daemon) {
280
310
 
281
311
  // Return credentials for algo trading market data
282
312
  const { RITHMIC_ENDPOINTS } = require('../rithmic');
313
+ const { getPropFirm } = require('../../config/propfirms');
314
+
315
+ // Get proper rithmicSystem from config
316
+ const propfirmKey = rithmicSession.propfirmKey || daemon.propfirm?.key || 'apex_rithmic';
317
+ const propfirmConfig = getPropFirm(propfirmKey);
318
+ const systemName = propfirmConfig?.rithmicSystem || daemon.propfirm?.name || rithmicSession.propfirm || 'Apex';
319
+
283
320
  daemon._send(socket, createMessage(MSG_TYPE.CREDENTIALS, {
284
321
  success: true,
285
322
  credentials: {
@@ -289,9 +326,10 @@ function createHandlers(daemon) {
289
326
  rithmicCredentials: {
290
327
  userId: rithmicSession.credentials.username,
291
328
  password: rithmicSession.credentials.password,
292
- systemName: daemon.propfirm?.name || rithmicSession.propfirm || 'Apex',
329
+ systemName,
293
330
  gateway: RITHMIC_ENDPOINTS?.CHICAGO || 'wss://rprotocol.rithmic.com:443',
294
331
  },
332
+ propfirmKey,
295
333
  }, id));
296
334
  }
297
335
 
@@ -335,10 +335,16 @@ class DaemonProxyService extends EventEmitter {
335
335
  // For daemon mode, return stored credentials
336
336
  if (this.credentials && this.propfirmKey) {
337
337
  const { RITHMIC_ENDPOINTS } = require('../rithmic');
338
+ const { getPropFirm } = require('../../config/propfirms');
339
+
340
+ // Get proper rithmicSystem from config
341
+ const propfirmConfig = getPropFirm(this.propfirmKey);
342
+ const systemName = propfirmConfig?.rithmicSystem || this.propfirm?.rithmicSystem || 'Apex';
343
+
338
344
  return {
339
345
  userId: this.credentials.username,
340
346
  password: this.credentials.password,
341
- systemName: this.propfirm?.systemName || 'Apex',
347
+ systemName,
342
348
  gateway: RITHMIC_ENDPOINTS?.CHICAGO || 'wss://rprotocol.rithmic.com:443',
343
349
  };
344
350
  }
@@ -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 {