hedgequantx 2.3.23 → 2.4.0
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/api.jsc +0 -0
- package/dist/lib/api2.jsc +0 -0
- package/dist/lib/core.jsc +0 -0
- package/dist/lib/core2.jsc +0 -0
- package/dist/lib/data.jsc +0 -0
- package/dist/lib/data2.jsc +0 -0
- package/dist/lib/decoder.jsc +0 -0
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.jsc +0 -0
- package/dist/lib/n/r1.jsc +0 -0
- package/dist/lib/n/r2.jsc +0 -0
- package/dist/lib/n/r3.jsc +0 -0
- package/dist/lib/n/r4.jsc +0 -0
- package/dist/lib/n/r5.jsc +0 -0
- package/dist/lib/n/r6.jsc +0 -0
- package/dist/lib/n/r7.jsc +0 -0
- package/dist/lib/o/util1.jsc +0 -0
- package/dist/lib/o/util2.jsc +0 -0
- package/package.json +1 -1
- package/src/pages/algo/one-account.js +168 -78
package/dist/lib/api.jsc
CHANGED
|
Binary file
|
package/dist/lib/api2.jsc
CHANGED
|
Binary file
|
package/dist/lib/core.jsc
CHANGED
|
Binary file
|
package/dist/lib/core2.jsc
CHANGED
|
Binary file
|
package/dist/lib/data.jsc
CHANGED
|
Binary file
|
package/dist/lib/data2.jsc
CHANGED
|
Binary file
|
package/dist/lib/decoder.jsc
CHANGED
|
Binary file
|
package/dist/lib/m/mod1.jsc
CHANGED
|
Binary file
|
package/dist/lib/m/mod2.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r1.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r2.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r3.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r4.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r5.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r6.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r7.jsc
CHANGED
|
Binary file
|
package/dist/lib/o/util1.jsc
CHANGED
|
Binary file
|
package/dist/lib/o/util2.jsc
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One Account Mode
|
|
2
|
+
* One Account Mode - HQX Ultra Scalping
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const chalk = require('chalk');
|
|
@@ -11,6 +11,10 @@ const { AlgoUI, renderSessionSummary } = require('./ui');
|
|
|
11
11
|
const { prompts } = require('../../utils');
|
|
12
12
|
const { checkMarketHours } = require('../../services/projectx/market');
|
|
13
13
|
|
|
14
|
+
// Strategy & Market Data (compiled bytecode)
|
|
15
|
+
const { M1 } = require('../../lib/m/s1');
|
|
16
|
+
const { MarketDataFeed } = require('../../lib/data');
|
|
17
|
+
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
/**
|
|
@@ -155,8 +159,8 @@ const configureAlgo = async (account, contract) => {
|
|
|
155
159
|
};
|
|
156
160
|
|
|
157
161
|
/**
|
|
158
|
-
* Launch algo trading
|
|
159
|
-
*
|
|
162
|
+
* Launch algo trading - HQX Ultra Scalping Strategy
|
|
163
|
+
* Real-time market data + Strategy signals + Auto order execution
|
|
160
164
|
*/
|
|
161
165
|
const launchAlgo = async (service, account, contract, config) => {
|
|
162
166
|
const { contracts, dailyTarget, maxRisk, showName } = config;
|
|
@@ -166,9 +170,11 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
166
170
|
? (account.accountName || account.rithmicAccountId || account.accountId)
|
|
167
171
|
: 'HQX *****';
|
|
168
172
|
const symbolName = contract.name;
|
|
173
|
+
const contractId = contract.id;
|
|
169
174
|
const connectionType = account.platform || 'ProjectX';
|
|
175
|
+
const tickSize = contract.tickSize || 0.25;
|
|
170
176
|
|
|
171
|
-
const ui = new AlgoUI({ subtitle: 'HQX
|
|
177
|
+
const ui = new AlgoUI({ subtitle: 'HQX Ultra Scalping', mode: 'one-account' });
|
|
172
178
|
|
|
173
179
|
const stats = {
|
|
174
180
|
accountName,
|
|
@@ -183,118 +189,195 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
183
189
|
wins: 0,
|
|
184
190
|
losses: 0,
|
|
185
191
|
latency: 0,
|
|
186
|
-
connected:
|
|
192
|
+
connected: false,
|
|
187
193
|
startTime: Date.now()
|
|
188
194
|
};
|
|
189
195
|
|
|
190
196
|
let running = true;
|
|
191
197
|
let stopReason = null;
|
|
192
|
-
let startingPnL = null;
|
|
193
|
-
let
|
|
194
|
-
let
|
|
198
|
+
let startingPnL = null;
|
|
199
|
+
let currentPosition = 0; // Current position qty (+ long, - short)
|
|
200
|
+
let pendingOrder = false; // Prevent duplicate orders
|
|
201
|
+
let tickCount = 0;
|
|
202
|
+
|
|
203
|
+
// Initialize Strategy
|
|
204
|
+
const strategy = new M1({ tickSize });
|
|
205
|
+
strategy.initialize(contractId, tickSize);
|
|
206
|
+
|
|
207
|
+
// Initialize Market Data Feed
|
|
208
|
+
const marketFeed = new MarketDataFeed({ propfirm: account.propfirm });
|
|
195
209
|
|
|
196
|
-
// Log startup
|
|
210
|
+
// Log startup
|
|
197
211
|
ui.addLog('info', `Connection: ${connectionType}`);
|
|
198
212
|
ui.addLog('info', `Account: ${accountName}`);
|
|
199
213
|
ui.addLog('info', `Symbol: ${symbolName} | Qty: ${contracts}`);
|
|
200
214
|
ui.addLog('info', `Target: $${dailyTarget} | Max Risk: $${maxRisk}`);
|
|
201
|
-
ui.addLog('info', '
|
|
215
|
+
ui.addLog('info', 'Connecting to market data...');
|
|
202
216
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
217
|
+
// Handle strategy signals
|
|
218
|
+
strategy.on('signal', async (signal) => {
|
|
219
|
+
if (!running || pendingOrder || currentPosition !== 0) return;
|
|
220
|
+
|
|
221
|
+
const { side, direction, entry, stopLoss, takeProfit, confidence } = signal;
|
|
222
|
+
|
|
223
|
+
ui.addLog('signal', `${direction.toUpperCase()} signal @ ${entry.toFixed(2)} (${(confidence * 100).toFixed(0)}%)`);
|
|
224
|
+
|
|
225
|
+
// Place order via API
|
|
226
|
+
pendingOrder = true;
|
|
206
227
|
try {
|
|
207
|
-
|
|
228
|
+
const orderSide = direction === 'long' ? 0 : 1; // 0=Buy, 1=Sell
|
|
229
|
+
const orderResult = await service.placeOrder({
|
|
230
|
+
accountId: account.accountId,
|
|
231
|
+
contractId: contractId,
|
|
232
|
+
type: 2, // Market order
|
|
233
|
+
side: orderSide,
|
|
234
|
+
size: contracts
|
|
235
|
+
});
|
|
208
236
|
|
|
209
|
-
|
|
237
|
+
if (orderResult.success) {
|
|
238
|
+
currentPosition = direction === 'long' ? contracts : -contracts;
|
|
239
|
+
stats.trades++;
|
|
240
|
+
ui.addLog('trade', `OPENED ${direction.toUpperCase()} ${contracts}x @ market`);
|
|
241
|
+
|
|
242
|
+
// Place bracket orders (SL/TP)
|
|
243
|
+
if (stopLoss && takeProfit) {
|
|
244
|
+
// Stop Loss
|
|
245
|
+
await service.placeOrder({
|
|
246
|
+
accountId: account.accountId,
|
|
247
|
+
contractId: contractId,
|
|
248
|
+
type: 4, // Stop order
|
|
249
|
+
side: direction === 'long' ? 1 : 0, // Opposite side
|
|
250
|
+
size: contracts,
|
|
251
|
+
stopPrice: stopLoss
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Take Profit
|
|
255
|
+
await service.placeOrder({
|
|
256
|
+
accountId: account.accountId,
|
|
257
|
+
contractId: contractId,
|
|
258
|
+
type: 1, // Limit order
|
|
259
|
+
side: direction === 'long' ? 1 : 0,
|
|
260
|
+
size: contracts,
|
|
261
|
+
limitPrice: takeProfit
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
ui.addLog('info', `SL: ${stopLoss.toFixed(2)} | TP: ${takeProfit.toFixed(2)}`);
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
ui.addLog('error', `Order failed: ${orderResult.error}`);
|
|
268
|
+
}
|
|
269
|
+
} catch (e) {
|
|
270
|
+
ui.addLog('error', `Order error: ${e.message}`);
|
|
271
|
+
}
|
|
272
|
+
pendingOrder = false;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Handle market data ticks
|
|
276
|
+
marketFeed.on('tick', (tick) => {
|
|
277
|
+
tickCount++;
|
|
278
|
+
const latencyStart = Date.now();
|
|
279
|
+
|
|
280
|
+
// Feed tick to strategy
|
|
281
|
+
strategy.processTick({
|
|
282
|
+
contractId: tick.contractId || contractId,
|
|
283
|
+
price: tick.price,
|
|
284
|
+
bid: tick.bid,
|
|
285
|
+
ask: tick.ask,
|
|
286
|
+
volume: tick.volume || 1,
|
|
287
|
+
side: tick.lastTradeSide || 'unknown',
|
|
288
|
+
timestamp: tick.timestamp || Date.now()
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
stats.latency = Date.now() - latencyStart;
|
|
292
|
+
|
|
293
|
+
// Log every 100th tick to show activity
|
|
294
|
+
if (tickCount % 100 === 0) {
|
|
295
|
+
ui.addLog('info', `Tick #${tickCount} @ ${tick.price?.toFixed(2) || 'N/A'}`);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
marketFeed.on('connected', () => {
|
|
300
|
+
stats.connected = true;
|
|
301
|
+
ui.addLog('success', 'Market data connected!');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
marketFeed.on('error', (err) => {
|
|
305
|
+
ui.addLog('error', `Market: ${err.message}`);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
marketFeed.on('disconnected', () => {
|
|
309
|
+
stats.connected = false;
|
|
310
|
+
ui.addLog('error', 'Market data disconnected');
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Connect to market data
|
|
314
|
+
try {
|
|
315
|
+
const token = service.token || service.getToken?.();
|
|
316
|
+
await marketFeed.connect(token, account.propfirm, contractId);
|
|
317
|
+
await marketFeed.subscribe(symbolName, contractId);
|
|
318
|
+
} catch (e) {
|
|
319
|
+
ui.addLog('error', `Failed to connect: ${e.message}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Poll account P&L from API
|
|
323
|
+
const pollPnL = async () => {
|
|
324
|
+
try {
|
|
210
325
|
const accountResult = await service.getTradingAccounts();
|
|
211
326
|
if (accountResult.success && accountResult.accounts) {
|
|
212
327
|
const acc = accountResult.accounts.find(a => a.accountId === account.accountId);
|
|
213
328
|
if (acc && acc.profitAndLoss !== undefined) {
|
|
214
|
-
|
|
329
|
+
if (startingPnL === null) startingPnL = acc.profitAndLoss;
|
|
330
|
+
stats.pnl = acc.profitAndLoss - startingPnL;
|
|
215
331
|
|
|
216
|
-
//
|
|
217
|
-
if (
|
|
218
|
-
|
|
332
|
+
// Record trade result in strategy
|
|
333
|
+
if (stats.pnl !== 0) {
|
|
334
|
+
strategy.recordTradeResult(stats.pnl);
|
|
219
335
|
}
|
|
220
|
-
|
|
221
|
-
// Session P&L = current - starting (both from API)
|
|
222
|
-
const sessionPnL = accountPnL - startingPnL;
|
|
223
|
-
stats.pnl = sessionPnL;
|
|
224
336
|
}
|
|
225
337
|
}
|
|
226
338
|
|
|
227
|
-
//
|
|
339
|
+
// Check positions
|
|
228
340
|
const posResult = await service.getPositions(account.accountId);
|
|
229
|
-
|
|
230
341
|
if (posResult.success && posResult.positions) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return posSymbol.includes(contract.name) || posSymbol.includes(contract.id);
|
|
342
|
+
const pos = posResult.positions.find(p => {
|
|
343
|
+
const sym = p.contractId || p.symbol || '';
|
|
344
|
+
return sym.includes(contract.name) || sym.includes(contractId);
|
|
235
345
|
});
|
|
236
346
|
|
|
237
|
-
if (
|
|
238
|
-
|
|
347
|
+
if (pos && pos.quantity !== 0) {
|
|
348
|
+
currentPosition = pos.quantity;
|
|
349
|
+
const side = pos.quantity > 0 ? 'LONG' : 'SHORT';
|
|
350
|
+
const pnl = pos.profitAndLoss || 0;
|
|
239
351
|
|
|
240
|
-
//
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
stats.trades++;
|
|
244
|
-
|
|
245
|
-
if (tradePnL > 0) {
|
|
246
|
-
stats.wins++;
|
|
247
|
-
ui.addLog('trade', `+$${tradePnL.toFixed(2)}`);
|
|
248
|
-
} else {
|
|
249
|
-
stats.losses++;
|
|
250
|
-
ui.addLog('loss', `-$${Math.abs(tradePnL).toFixed(2)}`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
lastPositionPnL = positionPnL;
|
|
255
|
-
|
|
256
|
-
// Log position info from API
|
|
257
|
-
if (position.quantity && position.quantity !== 0) {
|
|
258
|
-
const side = position.quantity > 0 ? 'LONG' : 'SHORT';
|
|
259
|
-
const qty = Math.abs(position.quantity);
|
|
260
|
-
ui.addLog('info', `${side} ${qty}x @ P&L: $${positionPnL.toFixed(2)}`);
|
|
261
|
-
}
|
|
352
|
+
// Check if position closed (win/loss)
|
|
353
|
+
if (pnl > 0) stats.wins = Math.max(stats.wins, 1);
|
|
354
|
+
else if (pnl < 0) stats.losses = Math.max(stats.losses, 1);
|
|
262
355
|
} else {
|
|
263
|
-
|
|
264
|
-
if (pollCount % 15 === 0) {
|
|
265
|
-
ui.addLog('info', `Waiting for ${symbolName} position... (Session P&L: $${stats.pnl.toFixed(2)})`);
|
|
266
|
-
}
|
|
267
|
-
lastPositionPnL = 0;
|
|
356
|
+
currentPosition = 0;
|
|
268
357
|
}
|
|
269
358
|
}
|
|
270
359
|
|
|
271
|
-
// Check target/risk
|
|
360
|
+
// Check target/risk
|
|
272
361
|
if (stats.pnl >= dailyTarget) {
|
|
273
362
|
stopReason = 'target';
|
|
274
363
|
running = false;
|
|
275
|
-
ui.addLog('success', `
|
|
364
|
+
ui.addLog('success', `TARGET REACHED! +$${stats.pnl.toFixed(2)}`);
|
|
276
365
|
} else if (stats.pnl <= -maxRisk) {
|
|
277
366
|
stopReason = 'risk';
|
|
278
367
|
running = false;
|
|
279
|
-
ui.addLog('error', `
|
|
368
|
+
ui.addLog('error', `MAX RISK! -$${Math.abs(stats.pnl).toFixed(2)}`);
|
|
280
369
|
}
|
|
281
|
-
|
|
282
|
-
// Update latency (measured during this poll)
|
|
283
|
-
stats.latency = Date.now() - pollStart;
|
|
284
|
-
|
|
285
370
|
} catch (e) {
|
|
286
|
-
|
|
287
|
-
ui.addLog('error', `API: ${e.message}`);
|
|
371
|
+
// Silently handle polling errors
|
|
288
372
|
}
|
|
289
373
|
};
|
|
290
374
|
|
|
375
|
+
// Start polling and UI refresh
|
|
291
376
|
const refreshInterval = setInterval(() => { if (running) ui.render(stats); }, 250);
|
|
377
|
+
const pnlInterval = setInterval(() => { if (running) pollPnL(); }, 2000);
|
|
378
|
+
pollPnL(); // Initial poll
|
|
292
379
|
|
|
293
|
-
//
|
|
294
|
-
pollAPI(); // Initial poll
|
|
295
|
-
const apiInterval = setInterval(() => { if (running) pollAPI(); }, 2000);
|
|
296
|
-
|
|
297
|
-
// Keyboard
|
|
380
|
+
// Keyboard handler
|
|
298
381
|
const setupKeyHandler = () => {
|
|
299
382
|
if (!process.stdin.isTTY) return;
|
|
300
383
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -303,7 +386,8 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
303
386
|
|
|
304
387
|
const onKey = (str, key) => {
|
|
305
388
|
if (key && (key.name === 'x' || key.name === 'X' || (key.ctrl && key.name === 'c'))) {
|
|
306
|
-
running = false;
|
|
389
|
+
running = false;
|
|
390
|
+
stopReason = 'manual';
|
|
307
391
|
}
|
|
308
392
|
};
|
|
309
393
|
process.stdin.on('keypress', onKey);
|
|
@@ -315,22 +399,29 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
315
399
|
|
|
316
400
|
const cleanupKeys = setupKeyHandler();
|
|
317
401
|
|
|
402
|
+
// Wait for stop
|
|
318
403
|
await new Promise(resolve => {
|
|
319
|
-
const check = setInterval(() => {
|
|
404
|
+
const check = setInterval(() => {
|
|
405
|
+
if (!running) {
|
|
406
|
+
clearInterval(check);
|
|
407
|
+
resolve();
|
|
408
|
+
}
|
|
409
|
+
}, 100);
|
|
320
410
|
});
|
|
321
411
|
|
|
412
|
+
// Cleanup
|
|
322
413
|
clearInterval(refreshInterval);
|
|
323
|
-
clearInterval(
|
|
414
|
+
clearInterval(pnlInterval);
|
|
415
|
+
await marketFeed.disconnect();
|
|
324
416
|
if (cleanupKeys) cleanupKeys();
|
|
325
417
|
ui.cleanup();
|
|
326
418
|
|
|
327
|
-
// Ensure stdin is ready for prompts after algo cleanup
|
|
328
419
|
if (process.stdin.isTTY) {
|
|
329
420
|
process.stdin.setRawMode(false);
|
|
330
421
|
}
|
|
331
422
|
process.stdin.resume();
|
|
332
423
|
|
|
333
|
-
//
|
|
424
|
+
// Duration
|
|
334
425
|
const durationMs = Date.now() - stats.startTime;
|
|
335
426
|
const hours = Math.floor(durationMs / 3600000);
|
|
336
427
|
const minutes = Math.floor((durationMs % 3600000) / 60000);
|
|
@@ -344,7 +435,6 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
344
435
|
// Summary
|
|
345
436
|
renderSessionSummary(stats, stopReason);
|
|
346
437
|
|
|
347
|
-
// Wait 3 seconds then return to menu (avoid stdin issues after raw mode)
|
|
348
438
|
console.log('\n Returning to menu in 3 seconds...');
|
|
349
439
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
350
440
|
};
|