hedgequantx 2.6.161 → 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.
- package/package.json +1 -1
- package/src/menus/ai-agent-connect.js +181 -0
- package/src/menus/ai-agent-models.js +219 -0
- package/src/menus/ai-agent-oauth.js +292 -0
- package/src/menus/ai-agent-ui.js +141 -0
- package/src/menus/ai-agent.js +88 -1489
- package/src/pages/algo/copy-engine.js +449 -0
- package/src/pages/algo/copy-trading.js +11 -543
- package/src/pages/algo/smart-logs-data.js +218 -0
- package/src/pages/algo/smart-logs.js +9 -214
- package/src/pages/algo/ui-constants.js +144 -0
- package/src/pages/algo/ui-summary.js +184 -0
- package/src/pages/algo/ui.js +42 -526
- package/src/pages/stats-calculations.js +191 -0
- package/src/pages/stats-ui.js +381 -0
- package/src/pages/stats.js +14 -507
- package/src/services/ai/client-analysis.js +194 -0
- package/src/services/ai/client-models.js +333 -0
- package/src/services/ai/client.js +6 -489
- package/src/services/ai/index.js +2 -257
- package/src/services/ai/proxy-install.js +249 -0
- package/src/services/ai/proxy-manager.js +29 -411
- package/src/services/ai/proxy-remote.js +161 -0
- package/src/services/ai/supervisor-optimize.js +215 -0
- package/src/services/ai/supervisor-sync.js +178 -0
- package/src/services/ai/supervisor.js +50 -515
- package/src/services/ai/validation.js +250 -0
- package/src/services/hqx-server-events.js +110 -0
- package/src/services/hqx-server-handlers.js +217 -0
- package/src/services/hqx-server-latency.js +136 -0
- package/src/services/hqx-server.js +51 -403
- package/src/services/position-constants.js +28 -0
- package/src/services/position-manager.js +105 -554
- package/src/services/position-momentum.js +206 -0
- package/src/services/projectx/accounts.js +142 -0
- package/src/services/projectx/index.js +40 -289
- package/src/services/projectx/trading.js +180 -0
- package/src/services/rithmic/handlers.js +2 -208
- package/src/services/rithmic/index.js +32 -542
- package/src/services/rithmic/latency-tracker.js +182 -0
- package/src/services/rithmic/specs.js +146 -0
- package/src/services/rithmic/trade-history.js +254 -0
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Ultra-fast message handler
|
|
290
|
-
* @private
|
|
291
|
-
*/
|
|
224
|
+
/** Ultra-fast message handler */
|
|
292
225
|
_handleMessage(data) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Start adaptive heartbeat
|
|
616
|
-
* @private
|
|
617
|
-
*/
|
|
323
|
+
/** Start adaptive heartbeat */
|
|
618
324
|
_startHeartbeat() {
|
|
619
|
-
this.
|
|
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
|
-
|
|
641
|
-
clearTimeout(this.pingInterval);
|
|
642
|
-
this.pingInterval = null;
|
|
643
|
-
}
|
|
330
|
+
latencyMgr.stopHeartbeat.call(this);
|
|
644
331
|
}
|
|
645
332
|
|
|
646
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
391
|
+
eventMgr.clearAll(this.listeners);
|
|
738
392
|
|
|
739
393
|
// Reset stats
|
|
740
|
-
this
|
|
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
|
+
};
|