hedgequantx 2.9.221 → 2.9.223
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/dist/lib/m/ultra-scalping.js +2 -1
- package/package.json +1 -1
- package/src/app.js +46 -6
- package/src/pages/algo/algo-executor.js +9 -4
- package/src/pages/algo/multi-symbol-executor.js +3 -1
- package/src/services/daemon/client.js +8 -0
- package/src/services/daemon/constants.js +2 -0
- package/src/services/daemon/handlers.js +34 -0
- package/src/services/daemon/server.js +4 -0
|
@@ -561,7 +561,8 @@ class HQXUltraScalpingStrategy extends EventEmitter {
|
|
|
561
561
|
// Set last bar time to now
|
|
562
562
|
this.lastBarTime.set(contractId, Date.now());
|
|
563
563
|
|
|
564
|
-
|
|
564
|
+
// Tick-based strategy uses bars only for warmup reference data (volatility, ranges)
|
|
565
|
+
this.emit('log', { type: 'info', message: `Reference data loaded (${histBars.length} periods) - tick engine ready` });
|
|
565
566
|
}
|
|
566
567
|
|
|
567
568
|
/**
|
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -39,9 +39,10 @@ let daemonClient = null;
|
|
|
39
39
|
* Create a proxy service that uses daemon for all operations
|
|
40
40
|
* @param {Object} client - DaemonClient instance
|
|
41
41
|
* @param {Object} propfirm - Propfirm info
|
|
42
|
+
* @param {Object} credentials - Optional credentials {username, password}
|
|
42
43
|
* @returns {Object} Service-like object
|
|
43
44
|
*/
|
|
44
|
-
function createDaemonProxyService(client, propfirm) {
|
|
45
|
+
function createDaemonProxyService(client, propfirm, credentials = null) {
|
|
45
46
|
const checkMarketHours = () => {
|
|
46
47
|
const now = new Date(), utcDay = now.getUTCDay(), utcHour = now.getUTCHours();
|
|
47
48
|
const isDST = now.getTimezoneOffset() < Math.max(
|
|
@@ -56,8 +57,11 @@ function createDaemonProxyService(client, propfirm) {
|
|
|
56
57
|
return { isOpen: true, message: 'Market is open' };
|
|
57
58
|
};
|
|
58
59
|
|
|
60
|
+
// Store credentials for algo trading (market data feed)
|
|
61
|
+
let storedCredentials = credentials;
|
|
62
|
+
|
|
59
63
|
return {
|
|
60
|
-
propfirm, propfirmKey: propfirm?.key, accounts: [], credentials:
|
|
64
|
+
propfirm, propfirmKey: propfirm?.key, accounts: [], credentials: storedCredentials,
|
|
61
65
|
async getTradingAccounts() { return client.getTradingAccounts(); },
|
|
62
66
|
async getPositions() { return client.getPositions(); },
|
|
63
67
|
async getOrders() { return client.getOrders(); },
|
|
@@ -70,9 +74,22 @@ function createDaemonProxyService(client, propfirm) {
|
|
|
70
74
|
getAccountPnL() { return { pnl: null, openPnl: null, closedPnl: null, balance: null }; },
|
|
71
75
|
getToken() { return 'daemon-connected'; },
|
|
72
76
|
getPropfirm() { return propfirm?.key || 'apex'; },
|
|
73
|
-
getRithmicCredentials() {
|
|
77
|
+
getRithmicCredentials() {
|
|
78
|
+
// Return credentials for algo trading market data connection
|
|
79
|
+
if (!storedCredentials) return null;
|
|
80
|
+
const { RITHMIC_ENDPOINTS } = require('./services/rithmic');
|
|
81
|
+
return {
|
|
82
|
+
userId: storedCredentials.username,
|
|
83
|
+
password: storedCredentials.password,
|
|
84
|
+
systemName: propfirm?.systemName || propfirm?.name || 'Apex',
|
|
85
|
+
gateway: RITHMIC_ENDPOINTS?.CHICAGO || 'wss://rprotocol.rithmic.com:443',
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
setCredentials(creds) { storedCredentials = creds; },
|
|
74
89
|
checkMarketHours,
|
|
75
90
|
async disconnect() { return { success: true }; },
|
|
91
|
+
// For algo - disconnect ticker before starting new market data connection
|
|
92
|
+
async disconnectTicker() { return { success: true }; },
|
|
76
93
|
};
|
|
77
94
|
}
|
|
78
95
|
|
|
@@ -266,8 +283,19 @@ const run = async () => {
|
|
|
266
283
|
// Daemon already has a connection, use it
|
|
267
284
|
const accountsResult = await daemonClient.getTradingAccounts();
|
|
268
285
|
if (accountsResult.success && accountsResult.accounts?.length > 0) {
|
|
286
|
+
// Get credentials for algo trading
|
|
287
|
+
let credentials = null;
|
|
288
|
+
try {
|
|
289
|
+
const credResult = await daemonClient.getCredentials();
|
|
290
|
+
if (credResult.success) {
|
|
291
|
+
credentials = credResult.credentials;
|
|
292
|
+
}
|
|
293
|
+
} catch (credErr) {
|
|
294
|
+
log.warn('Failed to get credentials', { error: credErr.message });
|
|
295
|
+
}
|
|
296
|
+
|
|
269
297
|
// Create a proxy service that uses daemon
|
|
270
|
-
currentService = createDaemonProxyService(daemonClient, status.propfirm);
|
|
298
|
+
currentService = createDaemonProxyService(daemonClient, status.propfirm, credentials);
|
|
271
299
|
connections.services.push({
|
|
272
300
|
type: 'rithmic',
|
|
273
301
|
service: currentService,
|
|
@@ -282,7 +310,18 @@ const run = async () => {
|
|
|
282
310
|
// Daemon not connected, try to restore session via daemon
|
|
283
311
|
const restoreResult = await daemonClient.restoreSession();
|
|
284
312
|
if (restoreResult.success) {
|
|
285
|
-
|
|
313
|
+
// Get credentials for algo trading
|
|
314
|
+
let credentials = null;
|
|
315
|
+
try {
|
|
316
|
+
const credResult = await daemonClient.getCredentials();
|
|
317
|
+
if (credResult.success) {
|
|
318
|
+
credentials = credResult.credentials;
|
|
319
|
+
}
|
|
320
|
+
} catch (credErr) {
|
|
321
|
+
log.warn('Failed to get credentials', { error: credErr.message });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
currentService = createDaemonProxyService(daemonClient, restoreResult.propfirm, credentials);
|
|
286
325
|
connections.services.push({
|
|
287
326
|
type: 'rithmic',
|
|
288
327
|
service: currentService,
|
|
@@ -348,7 +387,8 @@ const run = async () => {
|
|
|
348
387
|
if (daemonClient?.connected) {
|
|
349
388
|
result = await daemonClient.login(selectedPropfirm.key, credentials.username, credentials.password);
|
|
350
389
|
if (result.success) {
|
|
351
|
-
|
|
390
|
+
// Pass credentials for algo trading market data
|
|
391
|
+
currentService = createDaemonProxyService(daemonClient, result.propfirm, credentials);
|
|
352
392
|
connections.services.push({
|
|
353
393
|
type: 'rithmic', service: currentService,
|
|
354
394
|
propfirm: selectedPropfirm.name, propfirmKey: selectedPropfirm.key, connectedAt: new Date(),
|
|
@@ -78,7 +78,12 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
|
|
|
78
78
|
sessionLogger.log('CONFIG', `account=${account.accountId} rithmicId=${account.rithmicAccountId || 'N/A'}`);
|
|
79
79
|
|
|
80
80
|
strategy.on('log', (log) => {
|
|
81
|
-
|
|
81
|
+
// Debug logs only go to session file, not UI (too verbose)
|
|
82
|
+
if (log.type === 'debug') {
|
|
83
|
+
sessionLogger.log('DEBUG', log.message);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const type = log.type === 'info' ? 'analysis' : 'system';
|
|
82
87
|
ui.addLog(type, log.message);
|
|
83
88
|
sessionLogger.log(type.toUpperCase(), log.message);
|
|
84
89
|
});
|
|
@@ -352,12 +357,12 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
|
|
|
352
357
|
if (histBars && histBars.length > 0) {
|
|
353
358
|
strategy.preloadBars(contractId, histBars);
|
|
354
359
|
// Different message for tick-based vs bar-based strategies
|
|
355
|
-
const isTickBased = strategyId === 'ultra-scalping';
|
|
360
|
+
const isTickBased = strategyId === 'ultra-scalping' || strategyId === 'hqx-scalping';
|
|
356
361
|
const msg = isTickBased
|
|
357
|
-
? `
|
|
362
|
+
? `Reference data loaded - QUANT tick engine initializing...`
|
|
358
363
|
: `Loaded ${histBars.length} historical bars - ready to trade!`;
|
|
359
364
|
ui.addLog('system', msg);
|
|
360
|
-
sessionLogger.log('
|
|
365
|
+
sessionLogger.log('WARMUP', isTickBased ? `Tick engine warmup with ${histBars.length} ref periods` : `Preloaded ${histBars.length} bars`);
|
|
361
366
|
} else {
|
|
362
367
|
ui.addLog('system', 'No history - warming up with live ticks...');
|
|
363
368
|
}
|
|
@@ -74,13 +74,15 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
|
|
|
74
74
|
// Filter logs - only show important events (swings, zones, signals)
|
|
75
75
|
strategy.on('log', (log) => {
|
|
76
76
|
const msg = log.message || '';
|
|
77
|
+
// Skip debug logs in UI (too verbose)
|
|
78
|
+
if (log.type === 'debug') return;
|
|
77
79
|
// Skip bar close logs (too noisy with 5 symbols)
|
|
78
80
|
if (msg.includes('[BAR]')) return;
|
|
79
81
|
// Skip routine pivot checks
|
|
80
82
|
if (msg.includes('Checking pivot')) return;
|
|
81
83
|
// Show swing and zone events
|
|
82
84
|
const prefix = `[${symbolCode}] `;
|
|
83
|
-
ui.addLog(
|
|
85
|
+
ui.addLog('analysis', prefix + msg);
|
|
84
86
|
});
|
|
85
87
|
}
|
|
86
88
|
|
|
@@ -351,6 +351,14 @@ class DaemonClient extends EventEmitter {
|
|
|
351
351
|
return this._request(MSG_TYPE.SEARCH_CONTRACTS, { search });
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Get credentials for algo trading
|
|
356
|
+
* @returns {Promise<Object>}
|
|
357
|
+
*/
|
|
358
|
+
async getCredentials() {
|
|
359
|
+
return this._request(MSG_TYPE.GET_CREDENTIALS);
|
|
360
|
+
}
|
|
361
|
+
|
|
354
362
|
/**
|
|
355
363
|
* Subscribe to market data
|
|
356
364
|
* @param {string} symbol
|
|
@@ -42,6 +42,7 @@ const MSG_TYPE = {
|
|
|
42
42
|
GET_STATUS: 'get_status',
|
|
43
43
|
GET_CONTRACTS: 'get_contracts',
|
|
44
44
|
SEARCH_CONTRACTS: 'search_contracts',
|
|
45
|
+
GET_CREDENTIALS: 'get_credentials',
|
|
45
46
|
|
|
46
47
|
// Data responses
|
|
47
48
|
ACCOUNTS: 'accounts',
|
|
@@ -50,6 +51,7 @@ const MSG_TYPE = {
|
|
|
50
51
|
PNL: 'pnl',
|
|
51
52
|
STATUS: 'status',
|
|
52
53
|
CONTRACTS: 'contracts',
|
|
54
|
+
CREDENTIALS: 'credentials',
|
|
53
55
|
|
|
54
56
|
// Trading
|
|
55
57
|
PLACE_ORDER: 'place_order',
|
|
@@ -262,6 +262,39 @@ function createHandlers(daemon) {
|
|
|
262
262
|
daemon._send(socket, createMessage(MSG_TYPE.ORDER_RESULT, result, id));
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
// ==================== CREDENTIALS HANDLER ====================
|
|
266
|
+
|
|
267
|
+
async function handleGetCredentials(socket, id) {
|
|
268
|
+
// Load credentials from saved session
|
|
269
|
+
const { storage } = require('../session');
|
|
270
|
+
const sessions = storage.load();
|
|
271
|
+
const rithmicSession = sessions.find(s => s.type === 'rithmic' && s.credentials);
|
|
272
|
+
|
|
273
|
+
if (!rithmicSession || !rithmicSession.credentials) {
|
|
274
|
+
daemon._send(socket, createMessage(MSG_TYPE.CREDENTIALS, {
|
|
275
|
+
success: false,
|
|
276
|
+
error: 'No credentials available',
|
|
277
|
+
}, id));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Return credentials for algo trading market data
|
|
282
|
+
const { RITHMIC_ENDPOINTS } = require('../rithmic');
|
|
283
|
+
daemon._send(socket, createMessage(MSG_TYPE.CREDENTIALS, {
|
|
284
|
+
success: true,
|
|
285
|
+
credentials: {
|
|
286
|
+
username: rithmicSession.credentials.username,
|
|
287
|
+
password: rithmicSession.credentials.password,
|
|
288
|
+
},
|
|
289
|
+
rithmicCredentials: {
|
|
290
|
+
userId: rithmicSession.credentials.username,
|
|
291
|
+
password: rithmicSession.credentials.password,
|
|
292
|
+
systemName: daemon.propfirm?.name || rithmicSession.propfirm || 'Apex',
|
|
293
|
+
gateway: RITHMIC_ENDPOINTS?.CHICAGO || 'wss://rprotocol.rithmic.com:443',
|
|
294
|
+
},
|
|
295
|
+
}, id));
|
|
296
|
+
}
|
|
297
|
+
|
|
265
298
|
// ==================== CONTRACT HANDLERS ====================
|
|
266
299
|
|
|
267
300
|
async function handleGetContracts(socket, id) {
|
|
@@ -332,6 +365,7 @@ function createHandlers(daemon) {
|
|
|
332
365
|
handleGetPositions,
|
|
333
366
|
handleGetOrders,
|
|
334
367
|
handleGetPnL,
|
|
368
|
+
handleGetCredentials,
|
|
335
369
|
handlePlaceOrder,
|
|
336
370
|
handleCancelOrder,
|
|
337
371
|
handleCancelAll,
|
|
@@ -196,6 +196,10 @@ class DaemonServer extends EventEmitter {
|
|
|
196
196
|
await h.handleGetPnL(socket, id, data);
|
|
197
197
|
break;
|
|
198
198
|
|
|
199
|
+
case MSG_TYPE.GET_CREDENTIALS:
|
|
200
|
+
await h.handleGetCredentials(socket, id);
|
|
201
|
+
break;
|
|
202
|
+
|
|
199
203
|
case MSG_TYPE.PLACE_ORDER:
|
|
200
204
|
await h.handlePlaceOrder(socket, id, data);
|
|
201
205
|
break;
|