hedgequantx 2.4.29 → 2.4.31
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/pages/algo/copy-trading.js +29 -8
- package/src/pages/algo/logger.js +245 -0
- package/src/pages/algo/one-account.js +52 -27
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ const { connections } = require('../../services');
|
|
|
11
11
|
const { AlgoUI, renderSessionSummary } = require('./ui');
|
|
12
12
|
const { logger, prompts } = require('../../utils');
|
|
13
13
|
const { checkMarketHours } = require('../../services/projectx/market');
|
|
14
|
+
const { algoLogger } = require('./logger');
|
|
14
15
|
|
|
15
16
|
const log = logger.scope('CopyTrading');
|
|
16
17
|
|
|
@@ -299,10 +300,17 @@ const launchCopyTrading = async (config) => {
|
|
|
299
300
|
}
|
|
300
301
|
};
|
|
301
302
|
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
// Smart startup logs (same as HQX-TG)
|
|
304
|
+
const market = checkMarketHours();
|
|
305
|
+
const sessionName = market.session || 'AMERICAN';
|
|
306
|
+
const etTime = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', timeZone: 'America/New_York' });
|
|
307
|
+
|
|
308
|
+
algoLogger.connectingToEngine(ui, lead.account.accountId);
|
|
309
|
+
algoLogger.engineStarting(ui, stats.platform, dailyTarget, maxRisk);
|
|
310
|
+
algoLogger.marketOpen(ui, sessionName.toUpperCase(), etTime);
|
|
311
|
+
algoLogger.info(ui, 'COPY MODE', `Lead: ${lead.propfirm} -> Follower: ${follower.propfirm}`);
|
|
312
|
+
algoLogger.dataConnected(ui, 'API');
|
|
313
|
+
algoLogger.algoOperational(ui, stats.platform);
|
|
306
314
|
stats.connected = true;
|
|
307
315
|
|
|
308
316
|
// Track lead positions and copy to follower
|
|
@@ -321,7 +329,12 @@ const launchCopyTrading = async (config) => {
|
|
|
321
329
|
const existing = lastLeadPositions.find(p => p.contractId === pos.contractId);
|
|
322
330
|
if (!existing && pos.quantity !== 0) {
|
|
323
331
|
// New position opened - copy to follower
|
|
324
|
-
|
|
332
|
+
const side = pos.quantity > 0 ? 'LONG' : 'SHORT';
|
|
333
|
+
const symbol = pos.symbol || pos.contractId;
|
|
334
|
+
const size = Math.abs(pos.quantity);
|
|
335
|
+
const entry = pos.averagePrice || 0;
|
|
336
|
+
algoLogger.positionOpened(ui, symbol, side, size, entry);
|
|
337
|
+
algoLogger.info(ui, 'COPYING TO FOLLOWER', `${side} ${size}x ${symbol}`);
|
|
325
338
|
// TODO: Place order on follower account
|
|
326
339
|
}
|
|
327
340
|
}
|
|
@@ -330,7 +343,13 @@ const launchCopyTrading = async (config) => {
|
|
|
330
343
|
for (const oldPos of lastLeadPositions) {
|
|
331
344
|
const stillOpen = currentPositions.find(p => p.contractId === oldPos.contractId);
|
|
332
345
|
if (!stillOpen || stillOpen.quantity === 0) {
|
|
333
|
-
|
|
346
|
+
const side = oldPos.quantity > 0 ? 'LONG' : 'SHORT';
|
|
347
|
+
const symbol = oldPos.symbol || oldPos.contractId;
|
|
348
|
+
const size = Math.abs(oldPos.quantity);
|
|
349
|
+
const exit = stillOpen?.averagePrice || oldPos.averagePrice || 0;
|
|
350
|
+
const pnl = oldPos.profitAndLoss || 0;
|
|
351
|
+
algoLogger.positionClosed(ui, symbol, side, size, exit, pnl);
|
|
352
|
+
algoLogger.info(ui, 'CLOSING ON FOLLOWER', symbol);
|
|
334
353
|
// TODO: Close position on follower account
|
|
335
354
|
}
|
|
336
355
|
}
|
|
@@ -353,11 +372,13 @@ const launchCopyTrading = async (config) => {
|
|
|
353
372
|
if (stats.pnl >= dailyTarget) {
|
|
354
373
|
stopReason = 'target';
|
|
355
374
|
running = false;
|
|
356
|
-
|
|
375
|
+
algoLogger.targetHit(ui, lead.symbol.name, 0, stats.pnl);
|
|
376
|
+
algoLogger.info(ui, 'DAILY TARGET REACHED', `+$${stats.pnl.toFixed(2)} - Stopping algo`);
|
|
357
377
|
} else if (stats.pnl <= -maxRisk) {
|
|
358
378
|
stopReason = 'risk';
|
|
359
379
|
running = false;
|
|
360
|
-
|
|
380
|
+
algoLogger.dailyLimitWarning(ui, stats.pnl, -maxRisk);
|
|
381
|
+
algoLogger.error(ui, 'MAX RISK HIT', `-$${Math.abs(stats.pnl).toFixed(2)} - Stopping algo`);
|
|
361
382
|
}
|
|
362
383
|
} catch (e) {
|
|
363
384
|
// Silent fail - will retry
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* =============================================================================
|
|
3
|
+
* HQX Algo Trading System - Smart Algo Logger
|
|
4
|
+
* =============================================================================
|
|
5
|
+
* Provides rich, detailed logs for the algo UI
|
|
6
|
+
* Copied from HQX-TG algo-logger.ts - All 47 log types
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Smart Algo Logger - All 47 log types from HQX-TG
|
|
13
|
+
*/
|
|
14
|
+
const algoLogger = {
|
|
15
|
+
// === MARKET DATA LOGS ===
|
|
16
|
+
quote(ui, symbol, bid, ask, spread) {
|
|
17
|
+
ui.addLog('info', `QUOTE ${symbol} - bid: ${bid.toFixed(2)} | ask: ${ask.toFixed(2)} | spread: ${spread.toFixed(2)}`);
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
tape(ui, symbol, buyVol, sellVol, delta) {
|
|
21
|
+
const sign = delta > 0 ? '+' : '';
|
|
22
|
+
ui.addLog('info', `TAPE ${symbol} - buy: ${buyVol} | sell: ${sellVol} | delta: ${sign}${delta}`);
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
dom(ui, symbol, bidDepth, askDepth, imbalance) {
|
|
26
|
+
const direction = imbalance > 0 ? '▲' : imbalance < 0 ? '▼' : '=';
|
|
27
|
+
ui.addLog('info', `DOM ${symbol} - bidDepth: ${bidDepth} | askDepth: ${askDepth} | imbalance: ${direction}${Math.abs(imbalance).toFixed(1)}%`);
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
volumeSpike(ui, symbol, volume, avgVolume, ratio) {
|
|
31
|
+
ui.addLog('analysis', `VOLUME SPIKE ${symbol} - vol: ${volume} | avg: ${avgVolume.toFixed(0)} | ratio: ${ratio.toFixed(1)}x`);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// === ANALYSIS LOGS ===
|
|
35
|
+
orderFlow(ui, symbol, score, direction) {
|
|
36
|
+
const arrow = direction === 'LONG' ? '▲' : direction === 'SHORT' ? '▼' : '=';
|
|
37
|
+
ui.addLog('analysis', `ORDER FLOW ${symbol} - score: ${score.toFixed(0)} | direction: ${arrow} ${direction}`);
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
absorption(ui, symbol, level, side, strength) {
|
|
41
|
+
ui.addLog('analysis', `ABSORPTION ${side} - price: ${level.toFixed(2)} | strength: ${strength.toFixed(0)}%`);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
sweep(ui, symbol, side, levels, volume) {
|
|
45
|
+
ui.addLog('analysis', `SWEEP ${side} - levels: ${levels} | volume: ${volume}`);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
iceberg(ui, symbol, price, hiddenSize) {
|
|
49
|
+
ui.addLog('analysis', `ICEBERG DETECTED - price: ${price.toFixed(2)} | hidden: ${hiddenSize}`);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
deltaDivergence(ui, symbol, priceDir, deltaDir) {
|
|
53
|
+
ui.addLog('analysis', `DELTA DIVERGENCE - price: ${priceDir} | delta: ${deltaDir}`);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
vpoc(ui, symbol, poc, valueHigh, valueLow) {
|
|
57
|
+
ui.addLog('analysis', `VPOC ${symbol} - poc: ${poc.toFixed(2)} | VAH: ${valueHigh.toFixed(2)} | VAL: ${valueLow.toFixed(2)}`);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
regime(ui, symbol, regime, confidence) {
|
|
61
|
+
ui.addLog('analysis', `REGIME ${symbol} - ${regime} | confidence: ${confidence.toFixed(0)}%`);
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
volatility(ui, symbol, atr, regime) {
|
|
65
|
+
ui.addLog('analysis', `VOLATILITY ${symbol} - ATR: ${atr.toFixed(2)} | regime: ${regime}`);
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// === SIGNAL LOGS ===
|
|
69
|
+
signalGenerated(ui, symbol, direction, confidence, strategy) {
|
|
70
|
+
const arrow = direction === 'LONG' ? '▲' : '▼';
|
|
71
|
+
ui.addLog('signal', `SIGNAL ${arrow} ${direction} - ${symbol} | conf: ${confidence.toFixed(0)}% | strategy: ${strategy}`);
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
signalRejected(ui, symbol, reason) {
|
|
75
|
+
ui.addLog('warning', `SIGNAL REJECTED - ${symbol} | reason: ${reason}`);
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
signalExpired(ui, symbol) {
|
|
79
|
+
ui.addLog('info', `SIGNAL EXPIRED - ${symbol}`);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// === TRADE LOGS ===
|
|
83
|
+
orderSubmitted(ui, symbol, side, size, price) {
|
|
84
|
+
ui.addLog('trade', `ORDER SUBMITTED - ${side} ${size} ${symbol} @ ${price.toFixed(2)}`);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
orderFilled(ui, symbol, side, size, price) {
|
|
88
|
+
ui.addLog('trade', `ORDER FILLED - ${side} ${size} ${symbol} @ ${price.toFixed(2)}`);
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
orderRejected(ui, symbol, reason) {
|
|
92
|
+
ui.addLog('error', `ORDER REJECTED - ${symbol} | ${reason}`);
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
orderCancelled(ui, symbol, orderId) {
|
|
96
|
+
ui.addLog('warning', `ORDER CANCELLED - ${symbol} | id: ${orderId}`);
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
positionOpened(ui, symbol, side, size, entry) {
|
|
100
|
+
const arrow = side === 'LONG' ? '▲' : '▼';
|
|
101
|
+
ui.addLog('trade', `POSITION OPENED ${arrow} - ${side} ${size} ${symbol} @ ${entry.toFixed(2)}`);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
positionClosed(ui, symbol, side, size, exit, pnl) {
|
|
105
|
+
const pnlStr = pnl >= 0 ? `+$${pnl.toFixed(2)}` : `-$${Math.abs(pnl).toFixed(2)}`;
|
|
106
|
+
const type = pnl >= 0 ? 'trade' : 'warning';
|
|
107
|
+
ui.addLog(type, `POSITION CLOSED - ${side} ${size} ${symbol} @ ${exit.toFixed(2)} | PnL: ${pnlStr}`);
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
stopHit(ui, symbol, price, loss) {
|
|
111
|
+
ui.addLog('warning', `STOP LOSS HIT - ${symbol} @ ${price.toFixed(2)} | loss: -$${Math.abs(loss).toFixed(2)}`);
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
targetHit(ui, symbol, price, profit) {
|
|
115
|
+
ui.addLog('trade', `TARGET HIT - ${symbol} @ ${price.toFixed(2)} | profit: +$${profit.toFixed(2)}`);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
trailingStopMoved(ui, symbol, oldStop, newStop) {
|
|
119
|
+
ui.addLog('info', `TRAILING STOP MOVED - ${symbol} | ${oldStop.toFixed(2)} → ${newStop.toFixed(2)}`);
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
fillSync(ui, side, size, price, orderId) {
|
|
123
|
+
ui.addLog('trade', `FILL (SYNC) - ${side} ${size} @ ${price.toFixed(2)} (order #${orderId})`);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
entryConfirmed(ui, side, size, symbol, price) {
|
|
127
|
+
ui.addLog('info', `ENTRY CONFIRMED - ${side} ${size}x ${symbol} @ ${price.toFixed(2)}`);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
stopsSet(ui, sl, tp) {
|
|
131
|
+
ui.addLog('info', `STOPS SET - SL: ${sl.toFixed(2)} | TP: ${tp.toFixed(2)}`);
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// === RISK LOGS ===
|
|
135
|
+
riskCheck(ui, passed, reason) {
|
|
136
|
+
const type = passed ? 'info' : 'warning';
|
|
137
|
+
ui.addLog(type, `RISK CHECK ${passed ? 'PASSED' : 'BLOCKED'} - ${reason}`);
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
dailyLimitWarning(ui, current, limit) {
|
|
141
|
+
ui.addLog('warning', `DAILY LIMIT WARNING - PnL: $${current.toFixed(2)} / limit: $${limit.toFixed(2)}`);
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
maxDrawdownWarning(ui, current, max) {
|
|
145
|
+
ui.addLog('warning', `DRAWDOWN WARNING - current: ${current.toFixed(1)}% / max: ${max.toFixed(1)}%`);
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
positionSized(ui, contracts, kelly, riskAmount, riskPct) {
|
|
149
|
+
ui.addLog('info', `POSITION SIZE - ${contracts} contracts | kelly: ${kelly.toFixed(2)} | risk: $${riskAmount} (${riskPct}% of max)`);
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
bracketSet(ui, stopTicks, targetTicks, rr) {
|
|
153
|
+
ui.addLog('info', `BRACKET SET - SL: ${stopTicks}t | TP: ${targetTicks}t | R:R: ${rr.toFixed(1)}`);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
// === STRATEGY LOGS ===
|
|
157
|
+
strategySelected(ui, strategy, session, regime) {
|
|
158
|
+
ui.addLog('info', `STRATEGY SELECTED - ${strategy} | session: ${session} | regime: ${regime}`);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
strategySwitch(ui, from, to, reason) {
|
|
162
|
+
ui.addLog('info', `STRATEGY SWITCH - ${from} → ${to} | ${reason}`);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// === SESSION LOGS ===
|
|
166
|
+
sessionStart(ui, session) {
|
|
167
|
+
ui.addLog('info', `SESSION START - ${session}`);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
sessionEnd(ui, session) {
|
|
171
|
+
ui.addLog('info', `SESSION END - ${session}`);
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
marketOpen(ui, session, etTime) {
|
|
175
|
+
ui.addLog('info', `MARKET OPEN - ${session} SESSION | ET: ${etTime} ET`);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
marketClosed(ui) {
|
|
179
|
+
ui.addLog('warning', `MARKET CLOSED - Trading paused`);
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// === SYSTEM LOGS ===
|
|
183
|
+
connectingToEngine(ui, accountId) {
|
|
184
|
+
ui.addLog('info', `CONNECTING TO ALGO ENGINE... - Account: ${accountId}`);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
engineStarting(ui, platform, dailyTarget, dailyRisk) {
|
|
188
|
+
ui.addLog('info', `ALGO ENGINE STARTING... - Platform: ${platform} | Daily Target: $${dailyTarget} | Daily Risk: $${dailyRisk}`);
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
engineStarted(ui, platform, status) {
|
|
192
|
+
ui.addLog('info', `ALGO ENGINE STARTED - Status: ${status}`);
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
engineStopped(ui, reason) {
|
|
196
|
+
ui.addLog('info', `ENGINE STOPPED - ${reason || 'All positions flat'}`);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
algoOperational(ui, platform) {
|
|
200
|
+
ui.addLog('info', `ALGO FULLY OPERATIONAL - Connected to ${platform} - Scanning for alpha...`);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
dataConnected(ui, source) {
|
|
204
|
+
ui.addLog('info', `WEBSOCKET CONNECTED - Real-time updates enabled`);
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
dataDisconnected(ui, source, reason) {
|
|
208
|
+
ui.addLog('warning', `WEBSOCKET DISCONNECTED - Attempting to reconnect...`);
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
heartbeat(ui, tps, latency) {
|
|
212
|
+
ui.addLog('info', `HEARTBEAT - tps: ${tps} | latency: ${latency}ms`);
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
latencyReport(ui, latency) {
|
|
216
|
+
ui.addLog('analysis', `LATENCY - ${latency}ms order-to-fill`);
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
// === GENERIC ===
|
|
220
|
+
info(ui, message, details) {
|
|
221
|
+
ui.addLog('info', details ? `${message} - ${details}` : message);
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
warning(ui, message, details) {
|
|
225
|
+
ui.addLog('warning', details ? `${message} - ${details}` : message);
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
error(ui, message, details) {
|
|
229
|
+
ui.addLog('error', details ? `${message} - ${details}` : message);
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
signal(ui, message, details) {
|
|
233
|
+
ui.addLog('signal', details ? `${message} - ${details}` : message);
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
trade(ui, message, details) {
|
|
237
|
+
ui.addLog('trade', details ? `${message} - ${details}` : message);
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
analysis(ui, message, details) {
|
|
241
|
+
ui.addLog('analysis', details ? `${message} - ${details}` : message);
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
module.exports = { algoLogger };
|
|
@@ -14,6 +14,7 @@ const { checkMarketHours } = require('../../services/projectx/market');
|
|
|
14
14
|
// Strategy & Market Data (obfuscated)
|
|
15
15
|
const { M1 } = require('../../../dist/lib/m/s1');
|
|
16
16
|
const { MarketDataFeed } = require('../../../dist/lib/data');
|
|
17
|
+
const { algoLogger } = require('./logger');
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
|
|
@@ -155,6 +156,11 @@ const configureAlgo = async (account, contract) => {
|
|
|
155
156
|
const confirm = await prompts.confirmPrompt('Start algo trading?', true);
|
|
156
157
|
if (!confirm) return null;
|
|
157
158
|
|
|
159
|
+
// Show spinner while initializing
|
|
160
|
+
const initSpinner = ora({ text: 'Initializing algo trading...', color: 'yellow' }).start();
|
|
161
|
+
await new Promise(r => setTimeout(r, 500));
|
|
162
|
+
initSpinner.succeed('Launching algo...');
|
|
163
|
+
|
|
158
164
|
return { contracts, dailyTarget, maxRisk, showName };
|
|
159
165
|
};
|
|
160
166
|
|
|
@@ -208,12 +214,14 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
208
214
|
// Initialize Market Data Feed
|
|
209
215
|
const marketFeed = new MarketDataFeed({ propfirm: account.propfirm });
|
|
210
216
|
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
// Smart startup logs (same as HQX-TG)
|
|
218
|
+
const market = checkMarketHours();
|
|
219
|
+
const sessionName = market.session || 'AMERICAN';
|
|
220
|
+
const etTime = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', timeZone: 'America/New_York' });
|
|
221
|
+
|
|
222
|
+
algoLogger.connectingToEngine(ui, account.accountId);
|
|
223
|
+
algoLogger.engineStarting(ui, connectionType, dailyTarget, maxRisk);
|
|
224
|
+
algoLogger.marketOpen(ui, sessionName.toUpperCase(), etTime);
|
|
217
225
|
|
|
218
226
|
// Handle strategy signals
|
|
219
227
|
strategy.on('signal', async (signal) => {
|
|
@@ -221,7 +229,12 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
221
229
|
|
|
222
230
|
const { side, direction, entry, stopLoss, takeProfit, confidence } = signal;
|
|
223
231
|
|
|
224
|
-
|
|
232
|
+
// Calculate position size with kelly
|
|
233
|
+
const kelly = Math.min(0.25, confidence);
|
|
234
|
+
const riskAmount = Math.round(maxRisk * kelly);
|
|
235
|
+
const riskPct = Math.round((riskAmount / maxRisk) * 100);
|
|
236
|
+
|
|
237
|
+
algoLogger.positionSized(ui, contracts, kelly, riskAmount, riskPct);
|
|
225
238
|
|
|
226
239
|
// Place order via API
|
|
227
240
|
pendingOrder = true;
|
|
@@ -238,7 +251,13 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
238
251
|
if (orderResult.success) {
|
|
239
252
|
currentPosition = direction === 'long' ? contracts : -contracts;
|
|
240
253
|
stats.trades++;
|
|
241
|
-
|
|
254
|
+
const sideStr = direction === 'long' ? 'BUY' : 'SELL';
|
|
255
|
+
const positionSide = direction === 'long' ? 'LONG' : 'SHORT';
|
|
256
|
+
|
|
257
|
+
algoLogger.orderSubmitted(ui, symbolName, sideStr, contracts, entry);
|
|
258
|
+
algoLogger.orderFilled(ui, symbolName, sideStr, contracts, entry);
|
|
259
|
+
algoLogger.positionOpened(ui, symbolName, positionSide, contracts, entry);
|
|
260
|
+
algoLogger.entryConfirmed(ui, sideStr, contracts, symbolName, entry);
|
|
242
261
|
|
|
243
262
|
// Place bracket orders (SL/TP)
|
|
244
263
|
if (stopLoss && takeProfit) {
|
|
@@ -262,20 +281,24 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
262
281
|
limitPrice: takeProfit
|
|
263
282
|
});
|
|
264
283
|
|
|
265
|
-
|
|
284
|
+
algoLogger.stopsSet(ui, stopLoss, takeProfit);
|
|
266
285
|
}
|
|
267
286
|
} else {
|
|
268
|
-
|
|
287
|
+
algoLogger.orderRejected(ui, symbolName, orderResult.error || 'Unknown error');
|
|
269
288
|
}
|
|
270
289
|
} catch (e) {
|
|
271
|
-
|
|
290
|
+
algoLogger.error(ui, 'ORDER ERROR', e.message);
|
|
272
291
|
}
|
|
273
292
|
pendingOrder = false;
|
|
274
293
|
});
|
|
275
294
|
|
|
276
295
|
// Handle market data ticks
|
|
296
|
+
let lastHeartbeat = Date.now();
|
|
297
|
+
let tps = 0;
|
|
298
|
+
|
|
277
299
|
marketFeed.on('tick', (tick) => {
|
|
278
300
|
tickCount++;
|
|
301
|
+
tps++;
|
|
279
302
|
const latencyStart = Date.now();
|
|
280
303
|
|
|
281
304
|
// Feed tick to strategy
|
|
@@ -291,25 +314,27 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
291
314
|
|
|
292
315
|
stats.latency = Date.now() - latencyStart;
|
|
293
316
|
|
|
294
|
-
//
|
|
295
|
-
if (
|
|
296
|
-
|
|
317
|
+
// Heartbeat every 30 seconds (smart log instead of tick count)
|
|
318
|
+
if (Date.now() - lastHeartbeat > 30000) {
|
|
319
|
+
algoLogger.heartbeat(ui, tps, stats.latency);
|
|
320
|
+
lastHeartbeat = Date.now();
|
|
321
|
+
tps = 0;
|
|
297
322
|
}
|
|
298
323
|
});
|
|
299
324
|
|
|
300
325
|
marketFeed.on('connected', () => {
|
|
301
326
|
stats.connected = true;
|
|
302
|
-
|
|
327
|
+
algoLogger.dataConnected(ui, 'RTC');
|
|
328
|
+
algoLogger.algoOperational(ui, connectionType);
|
|
303
329
|
});
|
|
304
330
|
|
|
305
331
|
marketFeed.on('error', (err) => {
|
|
306
|
-
|
|
332
|
+
algoLogger.error(ui, 'MARKET ERROR', err.message);
|
|
307
333
|
});
|
|
308
334
|
|
|
309
335
|
marketFeed.on('disconnected', (err) => {
|
|
310
336
|
stats.connected = false;
|
|
311
|
-
|
|
312
|
-
ui.addLog('error', `DISC: ${reason.substring(0, 70).toUpperCase()}`);
|
|
337
|
+
algoLogger.dataDisconnected(ui, 'WEBSOCKET', err?.message);
|
|
313
338
|
});
|
|
314
339
|
|
|
315
340
|
// Connect to market data
|
|
@@ -318,14 +343,14 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
318
343
|
|
|
319
344
|
// CRITICAL: Get a fresh token for WebSocket connection
|
|
320
345
|
// TopStep invalidates WebSocket sessions for old tokens
|
|
321
|
-
|
|
346
|
+
algoLogger.info(ui, 'REFRESHING AUTH TOKEN...');
|
|
322
347
|
const token = await service.getFreshToken?.() || service.token || service.getToken?.();
|
|
323
348
|
|
|
324
349
|
if (!token) {
|
|
325
|
-
|
|
350
|
+
algoLogger.error(ui, 'NO AUTH TOKEN', 'Please reconnect');
|
|
326
351
|
} else {
|
|
327
|
-
|
|
328
|
-
|
|
352
|
+
algoLogger.info(ui, 'TOKEN OK', `${token.length} chars`);
|
|
353
|
+
algoLogger.info(ui, 'CONNECTING', `${propfirmKey.toUpperCase()} | ${contractId}`);
|
|
329
354
|
|
|
330
355
|
await marketFeed.connect(token, propfirmKey);
|
|
331
356
|
|
|
@@ -334,13 +359,13 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
334
359
|
|
|
335
360
|
if (marketFeed.isConnected()) {
|
|
336
361
|
await marketFeed.subscribe(symbolName, contractId);
|
|
337
|
-
|
|
362
|
+
algoLogger.info(ui, 'SUBSCRIBED', `${symbolName} real-time feed active`);
|
|
338
363
|
} else {
|
|
339
|
-
|
|
364
|
+
algoLogger.error(ui, 'CONNECTION LOST', 'Before subscribe');
|
|
340
365
|
}
|
|
341
366
|
}
|
|
342
367
|
} catch (e) {
|
|
343
|
-
|
|
368
|
+
algoLogger.error(ui, 'CONNECTION ERROR', e.message.substring(0, 50));
|
|
344
369
|
}
|
|
345
370
|
|
|
346
371
|
// Poll account P&L from API
|
|
@@ -385,11 +410,11 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
385
410
|
if (stats.pnl >= dailyTarget) {
|
|
386
411
|
stopReason = 'target';
|
|
387
412
|
running = false;
|
|
388
|
-
|
|
413
|
+
algoLogger.targetHit(ui, symbolName, 0, stats.pnl);
|
|
389
414
|
} else if (stats.pnl <= -maxRisk) {
|
|
390
415
|
stopReason = 'risk';
|
|
391
416
|
running = false;
|
|
392
|
-
|
|
417
|
+
algoLogger.dailyLimitWarning(ui, stats.pnl, -maxRisk);
|
|
393
418
|
}
|
|
394
419
|
} catch (e) {
|
|
395
420
|
// Silently handle polling errors
|