hedgequantx 2.9.124 → 2.9.126
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/lib/data.js +64 -29
- package/src/pages/algo/algo-executor.js +48 -44
package/package.json
CHANGED
package/src/lib/data.js
CHANGED
|
@@ -36,8 +36,9 @@ class MarketDataFeed extends EventEmitter {
|
|
|
36
36
|
/**
|
|
37
37
|
* Connect to Rithmic TICKER_PLANT
|
|
38
38
|
* @param {Object} rithmicCredentials - Credentials from RithmicService.getRithmicCredentials()
|
|
39
|
+
* @param {number} retries - Number of connection retries (default: 2)
|
|
39
40
|
*/
|
|
40
|
-
async connect(rithmicCredentials) {
|
|
41
|
+
async connect(rithmicCredentials, retries = 2) {
|
|
41
42
|
if (this.connected) return;
|
|
42
43
|
|
|
43
44
|
if (!rithmicCredentials || !rithmicCredentials.userId || !rithmicCredentials.password) {
|
|
@@ -45,7 +46,6 @@ class MarketDataFeed extends EventEmitter {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
this.credentials = rithmicCredentials;
|
|
48
|
-
this.connection = new RithmicConnection();
|
|
49
49
|
|
|
50
50
|
this.config = {
|
|
51
51
|
uri: rithmicCredentials.gateway || RITHMIC_ENDPOINTS.CHICAGO,
|
|
@@ -56,39 +56,74 @@ class MarketDataFeed extends EventEmitter {
|
|
|
56
56
|
appVersion: '2.0.0',
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
await proto.load();
|
|
62
|
-
|
|
63
|
-
await this.connection.connect(this.config);
|
|
59
|
+
// Ensure protobuf definitions are loaded
|
|
60
|
+
await proto.load();
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.emit('
|
|
78
|
-
resolve(true);
|
|
62
|
+
let lastError = null;
|
|
63
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
64
|
+
try {
|
|
65
|
+
if (attempt > 0) {
|
|
66
|
+
this.emit('debug', `TICKER_PLANT retry ${attempt}/${retries}...`);
|
|
67
|
+
await new Promise(r => setTimeout(r, 2000)); // Wait 2s before retry
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.connection = new RithmicConnection();
|
|
71
|
+
|
|
72
|
+
// Connection timeout handler
|
|
73
|
+
this.connection.on('error', (err) => {
|
|
74
|
+
this.emit('debug', `TICKER_PLANT connection error: ${err.message}`);
|
|
79
75
|
});
|
|
76
|
+
|
|
77
|
+
await this.connection.connect(this.config);
|
|
78
|
+
this.emit('debug', `TICKER_PLANT WebSocket connected to ${this.config.uri}`);
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
// Setup message handler for market data
|
|
81
|
+
this.connection.on('message', (msg) => this._handleMessage(msg));
|
|
82
|
+
|
|
83
|
+
// Handle disconnection for auto-reconnect
|
|
84
|
+
this.connection.on('disconnected', ({ code, reason }) => {
|
|
85
|
+
this.connected = false;
|
|
86
|
+
this.emit('disconnected', { code, reason });
|
|
87
|
+
// Auto-reconnect if not manual close
|
|
88
|
+
if (code !== 1000 && this.credentials) {
|
|
89
|
+
this.emit('debug', 'TICKER_PLANT disconnected, will reconnect in 3s...');
|
|
90
|
+
setTimeout(() => this.connect(this.credentials, 1), 3000);
|
|
91
|
+
}
|
|
84
92
|
});
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
// Login to TICKER_PLANT
|
|
95
|
+
return await new Promise((resolve, reject) => {
|
|
96
|
+
const timeout = setTimeout(() => {
|
|
97
|
+
reject(new Error('TICKER_PLANT login timeout'));
|
|
98
|
+
}, 15000);
|
|
99
|
+
|
|
100
|
+
this.connection.once('loggedIn', () => {
|
|
101
|
+
clearTimeout(timeout);
|
|
102
|
+
this.connected = true;
|
|
103
|
+
this.emit('connected');
|
|
104
|
+
resolve(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.connection.once('loginFailed', (data) => {
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
reject(new Error(data.message || 'TICKER_PLANT login failed'));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
this.connection.login('TICKER_PLANT');
|
|
113
|
+
});
|
|
114
|
+
} catch (e) {
|
|
115
|
+
lastError = e;
|
|
116
|
+
this.emit('debug', `TICKER_PLANT attempt ${attempt + 1} failed: ${e.message}`);
|
|
117
|
+
if (this.connection) {
|
|
118
|
+
try { await this.connection.disconnect(); } catch (_) {}
|
|
119
|
+
this.connection = null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
91
122
|
}
|
|
123
|
+
|
|
124
|
+
// All retries failed
|
|
125
|
+
this.emit('error', lastError);
|
|
126
|
+
throw lastError;
|
|
92
127
|
}
|
|
93
128
|
|
|
94
129
|
/**
|
|
@@ -234,7 +234,9 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
|
|
|
234
234
|
let lastAsk = null;
|
|
235
235
|
let ticksPerSecond = 0;
|
|
236
236
|
let lastTickSecond = Math.floor(Date.now() / 1000);
|
|
237
|
-
let
|
|
237
|
+
let lastBiasLogSecond = 0;
|
|
238
|
+
let lastDebugLogSecond = 0;
|
|
239
|
+
let lastStateLogSecond = 0;
|
|
238
240
|
let buyVolume = 0;
|
|
239
241
|
let sellVolume = 0;
|
|
240
242
|
|
|
@@ -283,64 +285,66 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
|
|
|
283
285
|
}
|
|
284
286
|
|
|
285
287
|
// Log tick count every 10 seconds to confirm data flow
|
|
286
|
-
if (
|
|
288
|
+
if (currentSecond - lastDebugLogSecond >= 10) {
|
|
289
|
+
lastDebugLogSecond = currentSecond;
|
|
287
290
|
const state = strategy.getAnalysisState?.(contractId, price);
|
|
288
291
|
const bars = state?.barsProcessed || 0;
|
|
289
292
|
ui.addLog('debug', `Ticks: ${tickCount} | Bars: ${bars} | Price: ${price?.toFixed(2)}`);
|
|
290
293
|
}
|
|
291
294
|
|
|
292
295
|
// === SMART LOGS - REDUCED FREQUENCY ===
|
|
293
|
-
|
|
294
|
-
|
|
296
|
+
// Log bias every 5 seconds
|
|
297
|
+
if (currentSecond - lastBiasLogSecond >= 5 && tickCount > 1) {
|
|
298
|
+
lastBiasLogSecond = currentSecond;
|
|
295
299
|
|
|
296
300
|
const totalVol = buyVolume + sellVolume;
|
|
297
301
|
const buyPressure = totalVol > 0 ? (buyVolume / totalVol) * 100 : 50;
|
|
298
302
|
const delta = buyVolume - sellVolume;
|
|
299
303
|
|
|
300
304
|
let bias = buyPressure > 55 ? 'LONG' : buyPressure < 45 ? 'SHORT' : 'FLAT';
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (ticksS <= 50) ui.addLog('analysis', `PROX S: ${Math.abs(gapS).toFixed(2)} pts (${ticksS} ticks) | Sweep BELOW then reject`);
|
|
332
|
-
}
|
|
333
|
-
if (state.activeZones === 0) ui.addLog('risk', 'Building liquidity map...');
|
|
334
|
-
else if (!state.nearestSupport && !state.nearestResistance) ui.addLog('risk', 'Zones outside range');
|
|
335
|
-
else if (!state.nearestSupport) ui.addLog('analysis', 'Monitoring R for SHORT sweep');
|
|
336
|
-
else if (!state.nearestResistance) ui.addLog('analysis', 'Monitoring S for LONG sweep');
|
|
337
|
-
else ui.addLog('ready', 'Both zones active - awaiting sweep');
|
|
305
|
+
const biasLog = smartLogs.getMarketBiasLog(bias, delta, buyPressure);
|
|
306
|
+
const biasType = bias === 'LONG' ? 'bullish' : bias === 'SHORT' ? 'bearish' : 'analysis';
|
|
307
|
+
ui.addLog(biasType, `${biasLog.message} ${biasLog.details || ''}`);
|
|
308
|
+
lastBias = bias;
|
|
309
|
+
// Reset volume after logging to avoid accumulation
|
|
310
|
+
buyVolume = 0;
|
|
311
|
+
sellVolume = 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Strategy state log every 30 seconds
|
|
315
|
+
if (currentSecond - lastStateLogSecond >= 30 && tickCount > 1) {
|
|
316
|
+
lastStateLogSecond = currentSecond;
|
|
317
|
+
const state = strategy.getAnalysisState?.(contractId, price);
|
|
318
|
+
if (state) {
|
|
319
|
+
const bars = state.barsProcessed || 0;
|
|
320
|
+
sessionLogger.state(state.activeZones || 0, state.swingsDetected || 0, bars, lastBias);
|
|
321
|
+
if (!state.ready) {
|
|
322
|
+
ui.addLog('system', `${state.message} (${bars} bars)`);
|
|
323
|
+
} else {
|
|
324
|
+
const resStr = state.nearestResistance ? state.nearestResistance.toFixed(2) : '--';
|
|
325
|
+
const supStr = state.nearestSupport ? state.nearestSupport.toFixed(2) : '--';
|
|
326
|
+
|
|
327
|
+
ui.addLog('analysis', `Zones: ${state.activeZones} | R: ${resStr} | S: ${supStr} | Swings: ${state.swingsDetected}`);
|
|
328
|
+
if (price && state.nearestResistance) {
|
|
329
|
+
const gapR = state.nearestResistance - price, ticksR = Math.abs(Math.round(gapR / tickSize));
|
|
330
|
+
if (ticksR <= 50) ui.addLog('analysis', `PROX R: ${Math.abs(gapR).toFixed(2)} pts (${ticksR} ticks) | Sweep ABOVE then reject`);
|
|
331
|
+
}
|
|
332
|
+
if (price && state.nearestSupport) {
|
|
333
|
+
const gapS = price - state.nearestSupport, ticksS = Math.abs(Math.round(gapS / tickSize));
|
|
334
|
+
if (ticksS <= 50) ui.addLog('analysis', `PROX S: ${Math.abs(gapS).toFixed(2)} pts (${ticksS} ticks) | Sweep BELOW then reject`);
|
|
338
335
|
}
|
|
336
|
+
if (state.activeZones === 0) ui.addLog('risk', 'Building liquidity map...');
|
|
337
|
+
else if (!state.nearestSupport && !state.nearestResistance) ui.addLog('risk', 'Zones outside range');
|
|
338
|
+
else if (!state.nearestSupport) ui.addLog('analysis', 'Monitoring R for SHORT sweep');
|
|
339
|
+
else if (!state.nearestResistance) ui.addLog('analysis', 'Monitoring S for LONG sweep');
|
|
340
|
+
else ui.addLog('ready', 'Both zones active - awaiting sweep');
|
|
339
341
|
}
|
|
340
342
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// AI status every 60s
|
|
346
|
+
if (currentSecond % 60 === 0 && supervisionEnabled && supervisionEngine) {
|
|
347
|
+
ui.addLog('analysis', `AI: ${supervisionEngine.getStatus().agents.map(a => a.name.split(' ')[0]).join(', ')}`);
|
|
344
348
|
}
|
|
345
349
|
|
|
346
350
|
lastPrice = price;
|