hedgequantx 2.6.160 → 2.6.162

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/menus/ai-agent-connect.js +181 -0
  3. package/src/menus/ai-agent-models.js +219 -0
  4. package/src/menus/ai-agent-oauth.js +292 -0
  5. package/src/menus/ai-agent-ui.js +141 -0
  6. package/src/menus/ai-agent.js +88 -1489
  7. package/src/pages/algo/copy-engine.js +449 -0
  8. package/src/pages/algo/copy-trading.js +11 -543
  9. package/src/pages/algo/smart-logs-data.js +218 -0
  10. package/src/pages/algo/smart-logs.js +9 -214
  11. package/src/pages/algo/ui-constants.js +144 -0
  12. package/src/pages/algo/ui-summary.js +184 -0
  13. package/src/pages/algo/ui.js +42 -526
  14. package/src/pages/stats-calculations.js +191 -0
  15. package/src/pages/stats-ui.js +381 -0
  16. package/src/pages/stats.js +14 -507
  17. package/src/services/ai/client-analysis.js +194 -0
  18. package/src/services/ai/client-models.js +333 -0
  19. package/src/services/ai/client.js +6 -489
  20. package/src/services/ai/index.js +2 -257
  21. package/src/services/ai/proxy-install.js +249 -0
  22. package/src/services/ai/proxy-manager.js +29 -411
  23. package/src/services/ai/proxy-remote.js +161 -0
  24. package/src/services/ai/strategy-supervisor.js +10 -765
  25. package/src/services/ai/supervisor-data.js +195 -0
  26. package/src/services/ai/supervisor-optimize.js +215 -0
  27. package/src/services/ai/supervisor-sync.js +178 -0
  28. package/src/services/ai/supervisor-utils.js +158 -0
  29. package/src/services/ai/supervisor.js +50 -515
  30. package/src/services/ai/validation.js +250 -0
  31. package/src/services/hqx-server-events.js +110 -0
  32. package/src/services/hqx-server-handlers.js +217 -0
  33. package/src/services/hqx-server-latency.js +136 -0
  34. package/src/services/hqx-server.js +51 -403
  35. package/src/services/position-constants.js +28 -0
  36. package/src/services/position-manager.js +105 -554
  37. package/src/services/position-momentum.js +206 -0
  38. package/src/services/projectx/accounts.js +142 -0
  39. package/src/services/projectx/index.js +40 -289
  40. package/src/services/projectx/trading.js +180 -0
  41. package/src/services/rithmic/handlers.js +2 -208
  42. package/src/services/rithmic/index.js +32 -542
  43. package/src/services/rithmic/latency-tracker.js +182 -0
  44. package/src/services/rithmic/specs.js +146 -0
  45. package/src/services/rithmic/trade-history.js +254 -0
@@ -1,13 +1,7 @@
1
1
  /**
2
- * @fileoverview HQX Server Service - Ultra Low Latency WebSocket for Scalping
2
+ * HQX Server Service - Ultra Low Latency WebSocket for Scalping
3
+ * Optimized for sub-ms message handling with TCP_NODELAY and binary format
3
4
  * @module services/hqx-server
4
- *
5
- * Optimized for sub-millisecond message handling:
6
- * - Binary message format (MessagePack)
7
- * - TCP_NODELAY enabled
8
- * - Pre-allocated buffers
9
- * - Zero-copy message handling
10
- * - Adaptive heartbeat
11
5
  */
12
6
 
13
7
  const WebSocket = require('ws');
@@ -16,63 +10,27 @@ const os = require('os');
16
10
  const { request } = require('../utils/http');
17
11
  const { HQX_SERVER, TIMEOUTS, SECURITY } = require('../config/settings');
18
12
  const { logger } = require('../utils/logger');
13
+ const latencyMgr = require('./hqx-server-latency');
14
+ const eventMgr = require('./hqx-server-events');
15
+ const handlers = require('./hqx-server-handlers');
19
16
 
20
17
  const log = logger.scope('HQX');
21
18
 
22
- // ==================== CONSTANTS ====================
23
-
24
- /** Message types as bytes for faster switching */
25
- const MSG_TYPE = {
26
- // Outgoing
27
- PING: 0x01,
28
- START_ALGO: 0x10,
29
- STOP_ALGO: 0x11,
30
- START_COPY: 0x12,
31
- ORDER: 0x20,
32
-
33
- // Incoming
34
- PONG: 0x81,
35
- SIGNAL: 0x90,
36
- TRADE: 0x91,
37
- FILL: 0x92,
38
- LOG: 0xA0,
39
- STATS: 0xA1,
40
- ERROR: 0xFF,
41
- };
19
+ // Re-export MSG_TYPE from handlers
20
+ const { MSG_TYPE } = handlers;
42
21
 
43
22
  /** Pre-allocated ping buffer */
44
23
  const PING_BUFFER = Buffer.alloc(9);
45
24
  PING_BUFFER.writeUInt8(MSG_TYPE.PING, 0);
46
25
 
47
- // ==================== FAST JSON ====================
48
-
49
- /**
50
- * Fast JSON stringify with pre-check
51
- * @param {Object} obj
52
- * @returns {string}
53
- */
26
+ /** Fast JSON stringify */
54
27
  const fastStringify = (obj) => {
55
- // For simple objects, manual is faster than JSON.stringify
56
28
  if (obj === null) return 'null';
57
29
  if (typeof obj !== 'object') return JSON.stringify(obj);
58
30
  return JSON.stringify(obj);
59
31
  };
60
32
 
61
- /**
62
- * Fast JSON parse with type hint
63
- * @param {string|Buffer} data
64
- * @returns {Object}
65
- */
66
- const fastParse = (data) => {
67
- const str = typeof data === 'string' ? data : data.toString('utf8');
68
- return JSON.parse(str);
69
- };
70
-
71
- // ==================== SERVICE ====================
72
-
73
- /**
74
- * HQX Server Service - Ultra Low Latency
75
- */
33
+ /** HQX Server Service - Ultra Low Latency */
76
34
  class HQXServerService {
77
35
  constructor() {
78
36
  // Connection
@@ -111,12 +69,7 @@ class HQXServerService {
111
69
  this.bytesReceived = 0;
112
70
  }
113
71
 
114
- // ==================== DEVICE ID ====================
115
-
116
- /**
117
- * Get cached device fingerprint
118
- * @returns {string}
119
- */
72
+ /** Get cached device fingerprint */
120
73
  _getDeviceId() {
121
74
  if (this._deviceId) return this._deviceId;
122
75
 
@@ -125,14 +78,7 @@ class HQXServerService {
125
78
  return this._deviceId;
126
79
  }
127
80
 
128
- // ==================== AUTH ====================
129
-
130
- /**
131
- * Authenticate with HQX Server
132
- * @param {string} userId
133
- * @param {string} [propfirm='unknown']
134
- * @returns {Promise<{success: boolean, sessionId?: string, error?: string}>}
135
- */
81
+ /** Authenticate with HQX Server */
136
82
  async authenticate(userId, propfirm = 'unknown') {
137
83
  const start = process.hrtime.bigint();
138
84
 
@@ -171,12 +117,7 @@ class HQXServerService {
171
117
  }
172
118
  }
173
119
 
174
- // ==================== WEBSOCKET ====================
175
-
176
- /**
177
- * Connect with ultra-low latency settings
178
- * @returns {Promise<{success: boolean, error?: string}>}
179
- */
120
+ /** Connect with ultra-low latency settings */
180
121
  async connect() {
181
122
  if (!this.token) {
182
123
  return { success: false, error: 'Not authenticated' };
@@ -258,10 +199,7 @@ class HQXServerService {
258
199
  });
259
200
  }
260
201
 
261
- /**
262
- * Apply TCP socket optimizations
263
- * @private
264
- */
202
+ /** Apply TCP socket optimizations */
265
203
  _optimizeSocket() {
266
204
  try {
267
205
  const socket = this.ws._socket;
@@ -283,166 +221,22 @@ class HQXServerService {
283
221
  }
284
222
  }
285
223
 
286
- // ==================== MESSAGE HANDLING ====================
287
-
288
- /**
289
- * Ultra-fast message handler
290
- * @private
291
- */
224
+ /** Ultra-fast message handler */
292
225
  _handleMessage(data) {
293
- const receiveTime = process.hrtime.bigint();
294
- this.messagesReceived++;
295
- this.bytesReceived += data.length;
296
-
297
- try {
298
- // Try binary format first (faster)
299
- if (Buffer.isBuffer(data) && data.length > 0) {
300
- const msgType = data.readUInt8(0);
301
-
302
- // Fast path for pong
303
- if (msgType === MSG_TYPE.PONG) {
304
- this._handlePong(data, receiveTime);
305
- return;
306
- }
307
-
308
- // Binary signal (fastest path)
309
- if (msgType === MSG_TYPE.SIGNAL) {
310
- this._handleBinarySignal(data);
311
- return;
312
- }
313
- }
314
-
315
- // JSON fallback
316
- const message = fastParse(data);
317
- this._handleJsonMessage(message, receiveTime);
318
-
319
- } catch (err) {
320
- log.warn('Message parse error', { error: err.message });
321
- }
322
- }
323
-
324
- /**
325
- * Handle pong with latency calculation
326
- * @private
327
- */
328
- _handlePong(data, receiveTime) {
329
- if (this.lastPingTime > 0) {
330
- // Use high-resolution timer
331
- const latency = Number(receiveTime - this.lastPingTime) / 1e6; // ns to ms
332
- this._updateLatency(latency);
333
- }
334
- }
335
-
336
- /**
337
- * Handle binary trading signal (zero-copy)
338
- * @private
339
- */
340
- _handleBinarySignal(data) {
341
- // Binary format: [type:1][timestamp:8][side:1][price:8][qty:4]
342
- if (data.length >= 22) {
343
- const signal = {
344
- timestamp: data.readBigInt64LE(1),
345
- side: data.readUInt8(9),
346
- price: data.readDoubleLE(10),
347
- quantity: data.readUInt32LE(18),
348
- };
349
- this._emit('signal', signal);
350
- }
351
- }
352
-
353
- /**
354
- * Handle JSON message
355
- * @private
356
- */
357
- _handleJsonMessage(message, receiveTime) {
358
- // Calculate latency from server timestamp
359
- if (message.timestamp) {
360
- const latency = Date.now() - message.timestamp;
361
- if (latency >= 0 && latency < 5000) {
362
- this._updateLatency(latency);
363
- }
364
- }
365
-
366
- // Fast dispatch
367
- switch (message.type) {
368
- case 'signal':
369
- this._emit('signal', message.data);
370
- break;
371
- case 'trade':
372
- this._emit('trade', message.data);
373
- break;
374
- case 'fill':
375
- this._emit('fill', message.data);
376
- break;
377
- case 'log':
378
- this._emit('log', message.data);
379
- break;
380
- case 'stats':
381
- this._emit('stats', message.data);
382
- break;
383
- case 'error':
384
- this._emit('error', message.data);
385
- break;
386
- case 'pong':
387
- // Already handled in binary path
388
- break;
389
- default:
390
- this._emit('message', message);
391
- }
226
+ handlers.processMessage(
227
+ this,
228
+ data,
229
+ (event, payload) => this._emit(event, payload),
230
+ (latency) => this._updateLatency(latency)
231
+ );
392
232
  }
393
233
 
394
- /**
395
- * Update latency statistics
396
- * @private
397
- */
234
+ /** Update latency statistics */
398
235
  _updateLatency(latency) {
399
- this.latency = latency;
400
- this.minLatency = Math.min(this.minLatency, latency);
401
- this.maxLatency = Math.max(this.maxLatency, latency);
402
-
403
- // Rolling average (last 100 samples)
404
- this.latencySamples.push(latency);
405
- if (this.latencySamples.length > 100) {
406
- this.latencySamples.shift();
407
- }
408
- this.avgLatency = this.latencySamples.reduce((a, b) => a + b, 0) / this.latencySamples.length;
409
-
410
- // Adapt heartbeat based on latency
411
- this._adaptHeartbeat();
412
-
413
- this._emit('latency', {
414
- current: latency,
415
- min: this.minLatency,
416
- max: this.maxLatency,
417
- avg: this.avgLatency
418
- });
236
+ latencyMgr.updateLatency.call(this, latency);
419
237
  }
420
238
 
421
- /**
422
- * Adapt heartbeat interval based on connection quality
423
- * @private
424
- */
425
- _adaptHeartbeat() {
426
- // Good connection: slower heartbeat (less overhead)
427
- // Poor connection: faster heartbeat (detect issues quickly)
428
- if (this.avgLatency < 10) {
429
- this.adaptiveHeartbeat = 2000; // <10ms: 2s heartbeat
430
- } else if (this.avgLatency < 50) {
431
- this.adaptiveHeartbeat = 1000; // <50ms: 1s heartbeat
432
- } else if (this.avgLatency < 100) {
433
- this.adaptiveHeartbeat = 500; // <100ms: 500ms heartbeat
434
- } else {
435
- this.adaptiveHeartbeat = 250; // High latency: 250ms heartbeat
436
- }
437
- }
438
-
439
- // ==================== SENDING ====================
440
-
441
- /**
442
- * Send message with minimal overhead
443
- * @param {string} type - Message type
444
- * @param {Object} data - Payload
445
- */
239
+ /** Send message with minimal overhead */
446
240
  send(type, data) {
447
241
  const message = {
448
242
  type,
@@ -459,10 +253,7 @@ class HQXServerService {
459
253
  }
460
254
  }
461
255
 
462
- /**
463
- * Send raw data (no JSON overhead)
464
- * @private
465
- */
256
+ /** Send raw data */
466
257
  _sendRaw(data) {
467
258
  try {
468
259
  this.ws.send(data);
@@ -471,10 +262,7 @@ class HQXServerService {
471
262
  }
472
263
  }
473
264
 
474
- /**
475
- * Send binary ping for lowest latency measurement
476
- * @private
477
- */
265
+ /** Send binary ping for lowest latency measurement */
478
266
  _sendBinaryPing() {
479
267
  if (!this.connected || this.ws?.readyState !== WebSocket.OPEN) return;
480
268
 
@@ -490,10 +278,7 @@ class HQXServerService {
490
278
  }
491
279
  }
492
280
 
493
- /**
494
- * Flush message queue
495
- * @private
496
- */
281
+ /** Flush message queue */
497
282
  _flushQueue() {
498
283
  while (this.messageQueue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
499
284
  const message = this.messageQueue.shift();
@@ -502,153 +287,50 @@ class HQXServerService {
502
287
  }
503
288
  }
504
289
 
505
- // ==================== ALGO CONTROL ====================
506
-
507
- /**
508
- * Start algo trading session
509
- * @param {Object} config
510
- */
290
+ /** Start algo trading session */
511
291
  startAlgo(config) {
512
292
  log.info('Starting algo', { symbol: config.symbol, contracts: config.contracts });
513
-
514
- this.send('start_algo', {
515
- accountId: config.accountId,
516
- contractId: config.contractId,
517
- symbol: config.symbol,
518
- contracts: config.contracts,
519
- dailyTarget: config.dailyTarget,
520
- maxRisk: config.maxRisk,
521
- propfirm: config.propfirm,
522
- propfirmToken: config.propfirmToken,
523
- rithmicCredentials: config.rithmicCredentials || null,
524
- copyTrading: config.copyTrading || false,
525
- followerSymbol: config.followerSymbol,
526
- followerContracts: config.followerContracts,
527
- });
293
+ this.send('start_algo', handlers.buildAlgoPayload(config));
528
294
  }
529
295
 
530
- /**
531
- * Stop algo trading
532
- */
296
+ /** Stop algo trading */
533
297
  stopAlgo() {
534
298
  log.info('Stopping algo');
535
299
  this.send('stop_algo', {});
536
300
  }
537
301
 
538
- /**
539
- * Start copy trading
540
- * @param {Object} config
541
- */
302
+ /** Start copy trading */
542
303
  startCopyTrading(config) {
543
304
  log.info('Starting copy trading');
544
-
545
- this.send('start_copy_trading', {
546
- // Lead
547
- leadAccountId: config.leadAccountId,
548
- leadContractId: config.leadContractId,
549
- leadSymbol: config.leadSymbol,
550
- leadContracts: config.leadContracts,
551
- leadPropfirm: config.leadPropfirm,
552
- leadToken: config.leadToken,
553
- leadRithmicCredentials: config.leadRithmicCredentials,
554
- // Follower
555
- followerAccountId: config.followerAccountId,
556
- followerContractId: config.followerContractId,
557
- followerSymbol: config.followerSymbol,
558
- followerContracts: config.followerContracts,
559
- followerPropfirm: config.followerPropfirm,
560
- followerToken: config.followerToken,
561
- followerRithmicCredentials: config.followerRithmicCredentials,
562
- // Targets
563
- dailyTarget: config.dailyTarget,
564
- maxRisk: config.maxRisk,
565
- });
305
+ this.send('start_copy_trading', handlers.buildCopyTradingPayload(config));
566
306
  }
567
307
 
568
- // ==================== EVENTS ====================
569
-
570
- /**
571
- * Register event listener
572
- * @param {string} event
573
- * @param {Function} callback
574
- */
308
+ /** Register event listener */
575
309
  on(event, callback) {
576
- if (!this.listeners.has(event)) {
577
- this.listeners.set(event, []);
578
- }
579
- this.listeners.get(event).push(callback);
310
+ eventMgr.on(this.listeners, event, callback);
580
311
  }
581
312
 
582
- /**
583
- * Remove event listener
584
- * @param {string} event
585
- * @param {Function} callback
586
- */
313
+ /** Remove event listener */
587
314
  off(event, callback) {
588
- const callbacks = this.listeners.get(event);
589
- if (callbacks) {
590
- const index = callbacks.indexOf(callback);
591
- if (index > -1) callbacks.splice(index, 1);
592
- }
315
+ eventMgr.off(this.listeners, event, callback);
593
316
  }
594
317
 
595
- /**
596
- * Emit event (inlined for speed)
597
- * @private
598
- */
318
+ /** Emit event */
599
319
  _emit(event, data) {
600
- const callbacks = this.listeners.get(event);
601
- if (!callbacks) return;
602
-
603
- for (let i = 0; i < callbacks.length; i++) {
604
- try {
605
- callbacks[i](data);
606
- } catch {
607
- // Don't let callback errors break the loop
608
- }
609
- }
320
+ eventMgr.emit(this.listeners, event, data);
610
321
  }
611
322
 
612
- // ==================== HEARTBEAT ====================
613
-
614
- /**
615
- * Start adaptive heartbeat
616
- * @private
617
- */
323
+ /** Start adaptive heartbeat */
618
324
  _startHeartbeat() {
619
- this._stopHeartbeat();
620
-
621
- const heartbeat = () => {
622
- if (this.connected) {
623
- this._sendBinaryPing();
624
-
625
- // Schedule next with adaptive interval
626
- this.pingInterval = setTimeout(heartbeat, this.adaptiveHeartbeat);
627
- }
628
- };
629
-
630
- // First ping immediately
631
- this._sendBinaryPing();
632
- this.pingInterval = setTimeout(heartbeat, this.adaptiveHeartbeat);
325
+ latencyMgr.startHeartbeat.call(this, () => this._sendBinaryPing());
633
326
  }
634
327
 
635
- /**
636
- * Stop heartbeat
637
- * @private
638
- */
328
+ /** Stop heartbeat */
639
329
  _stopHeartbeat() {
640
- if (this.pingInterval) {
641
- clearTimeout(this.pingInterval);
642
- this.pingInterval = null;
643
- }
330
+ latencyMgr.stopHeartbeat.call(this);
644
331
  }
645
332
 
646
- // ==================== RECONNECT ====================
647
-
648
- /**
649
- * Attempt reconnection with exponential backoff
650
- * @private
651
- */
333
+ /** Attempt reconnection with exponential backoff */
652
334
  _attemptReconnect() {
653
335
  if (this.reconnectAttempts >= SECURITY.MAX_RECONNECT_ATTEMPTS) {
654
336
  log.error('Max reconnect attempts reached');
@@ -676,50 +358,22 @@ class HQXServerService {
676
358
  }, delay);
677
359
  }
678
360
 
679
- // ==================== STATS ====================
680
-
681
- /**
682
- * Get latency statistics
683
- * @returns {Object}
684
- */
361
+ /** Get latency statistics */
685
362
  getLatencyStats() {
686
- return {
687
- current: this.latency,
688
- min: this.minLatency === Infinity ? 0 : this.minLatency,
689
- max: this.maxLatency,
690
- avg: this.avgLatency,
691
- samples: this.latencySamples.length,
692
- };
363
+ return latencyMgr.getLatencyStats.call(this);
693
364
  }
694
365
 
695
- /**
696
- * Get connection statistics
697
- * @returns {Object}
698
- */
366
+ /** Get connection statistics */
699
367
  getStats() {
700
- return {
701
- connected: this.connected,
702
- messagesSent: this.messagesSent,
703
- messagesReceived: this.messagesReceived,
704
- bytesReceived: this.bytesReceived,
705
- heartbeatInterval: this.adaptiveHeartbeat,
706
- latency: this.getLatencyStats(),
707
- };
368
+ return latencyMgr.getConnectionStats.call(this);
708
369
  }
709
370
 
710
- /**
711
- * Get current latency
712
- * @returns {number}
713
- */
371
+ /** Get current latency */
714
372
  getLatency() {
715
373
  return this.latency;
716
374
  }
717
375
 
718
- // ==================== CLEANUP ====================
719
-
720
- /**
721
- * Disconnect and cleanup
722
- */
376
+ /** Disconnect and cleanup */
723
377
  disconnect() {
724
378
  log.info('Disconnecting');
725
379
 
@@ -734,19 +388,13 @@ class HQXServerService {
734
388
  this.token = null;
735
389
  this.sessionId = null;
736
390
  this.messageQueue = [];
737
- this.listeners.clear();
391
+ eventMgr.clearAll(this.listeners);
738
392
 
739
393
  // Reset stats
740
- this.latencySamples = [];
741
- this.minLatency = Infinity;
742
- this.maxLatency = 0;
743
- this.avgLatency = 0;
394
+ latencyMgr.resetLatencyStats.call(this);
744
395
  }
745
396
 
746
- /**
747
- * Check if connected
748
- * @returns {boolean}
749
- */
397
+ /** Check if connected */
750
398
  isConnected() {
751
399
  return this.connected && this.ws?.readyState === WebSocket.OPEN;
752
400
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Position Manager Constants
3
+ * Momentum thresholds and weights for position management
4
+ *
5
+ * Data source: Calibrated from backtesting and live trading analysis
6
+ */
7
+
8
+ // =============================================================================
9
+ // MOMENTUM THRESHOLDS (from analysis)
10
+ // =============================================================================
11
+ const MOMENTUM = {
12
+ STRONG_FAVORABLE: 0.5, // momentum > 0.5 + profit → HOLD
13
+ WEAK_THRESHOLD: 0.2, // momentum < 0.2 + profit → EXIT with profit
14
+ ADVERSE_THRESHOLD: -0.3, // momentum < -0.3 → EXIT immediately
15
+ VPIN_DANGER: 0.7, // VPIN > 0.7 → informed traders = EXIT
16
+ };
17
+
18
+ // Momentum weights
19
+ const WEIGHTS = {
20
+ OFI: 0.50, // Order Flow Imbalance - 50%
21
+ KALMAN: 0.25, // Kalman Velocity - 25%
22
+ ZSCORE: 0.25, // Z-Score progression - 25%
23
+ };
24
+
25
+ module.exports = {
26
+ MOMENTUM,
27
+ WEIGHTS,
28
+ };