hedgequantx 2.6.162 → 2.7.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.
Files changed (138) hide show
  1. package/README.md +15 -88
  2. package/bin/cli.js +0 -11
  3. package/dist/lib/api.jsc +0 -0
  4. package/dist/lib/api2.jsc +0 -0
  5. package/dist/lib/core.jsc +0 -0
  6. package/dist/lib/core2.jsc +0 -0
  7. package/dist/lib/data.js +1 -1
  8. package/dist/lib/data.jsc +0 -0
  9. package/dist/lib/data2.jsc +0 -0
  10. package/dist/lib/decoder.jsc +0 -0
  11. package/dist/lib/m/mod1.jsc +0 -0
  12. package/dist/lib/m/mod2.jsc +0 -0
  13. package/dist/lib/n/r1.jsc +0 -0
  14. package/dist/lib/n/r2.jsc +0 -0
  15. package/dist/lib/n/r3.jsc +0 -0
  16. package/dist/lib/n/r4.jsc +0 -0
  17. package/dist/lib/n/r5.jsc +0 -0
  18. package/dist/lib/n/r6.jsc +0 -0
  19. package/dist/lib/n/r7.jsc +0 -0
  20. package/dist/lib/o/util1.jsc +0 -0
  21. package/dist/lib/o/util2.jsc +0 -0
  22. package/package.json +6 -3
  23. package/src/app.js +40 -162
  24. package/src/config/constants.js +31 -33
  25. package/src/config/propfirms.js +13 -217
  26. package/src/config/settings.js +0 -43
  27. package/src/lib/api.js +198 -0
  28. package/src/lib/api2.js +353 -0
  29. package/src/lib/core.js +539 -0
  30. package/src/lib/core2.js +341 -0
  31. package/src/lib/data.js +555 -0
  32. package/src/lib/data2.js +492 -0
  33. package/src/lib/decoder.js +599 -0
  34. package/src/lib/m/s1.js +804 -0
  35. package/src/lib/m/s2.js +34 -0
  36. package/src/lib/n/r1.js +454 -0
  37. package/src/lib/n/r2.js +514 -0
  38. package/src/lib/n/r3.js +631 -0
  39. package/src/lib/n/r4.js +401 -0
  40. package/src/lib/n/r5.js +335 -0
  41. package/src/lib/n/r6.js +425 -0
  42. package/src/lib/n/r7.js +530 -0
  43. package/src/lib/o/l1.js +44 -0
  44. package/src/lib/o/l2.js +427 -0
  45. package/src/lib/python-bridge.js +206 -0
  46. package/src/menus/connect.js +14 -176
  47. package/src/menus/dashboard.js +65 -110
  48. package/src/pages/accounts.js +18 -18
  49. package/src/pages/algo/copy-trading.js +210 -240
  50. package/src/pages/algo/index.js +41 -104
  51. package/src/pages/algo/one-account.js +386 -33
  52. package/src/pages/algo/ui.js +312 -151
  53. package/src/pages/orders.js +3 -3
  54. package/src/pages/positions.js +3 -3
  55. package/src/pages/stats/chart.js +74 -0
  56. package/src/pages/stats/display.js +228 -0
  57. package/src/pages/stats/index.js +236 -0
  58. package/src/pages/stats/metrics.js +213 -0
  59. package/src/pages/user.js +6 -6
  60. package/src/services/hqx-server/constants.js +55 -0
  61. package/src/services/hqx-server/index.js +401 -0
  62. package/src/services/hqx-server/latency.js +81 -0
  63. package/src/services/index.js +12 -3
  64. package/src/services/rithmic/accounts.js +7 -32
  65. package/src/services/rithmic/connection.js +1 -204
  66. package/src/services/rithmic/contracts.js +235 -0
  67. package/src/services/rithmic/handlers.js +21 -196
  68. package/src/services/rithmic/index.js +60 -291
  69. package/src/services/rithmic/market.js +31 -0
  70. package/src/services/rithmic/orders.js +5 -361
  71. package/src/services/rithmic/protobuf.js +5 -195
  72. package/src/services/session.js +22 -173
  73. package/src/ui/box.js +10 -18
  74. package/src/ui/index.js +1 -3
  75. package/src/ui/menu.js +1 -1
  76. package/src/utils/prompts.js +2 -2
  77. package/dist/lib/m/s1.js +0 -1
  78. package/src/menus/ai-agent-connect.js +0 -181
  79. package/src/menus/ai-agent-models.js +0 -219
  80. package/src/menus/ai-agent-oauth.js +0 -292
  81. package/src/menus/ai-agent-ui.js +0 -141
  82. package/src/menus/ai-agent.js +0 -484
  83. package/src/pages/algo/algo-config.js +0 -195
  84. package/src/pages/algo/algo-multi.js +0 -801
  85. package/src/pages/algo/algo-utils.js +0 -58
  86. package/src/pages/algo/copy-engine.js +0 -449
  87. package/src/pages/algo/custom-strategy.js +0 -459
  88. package/src/pages/algo/logger.js +0 -245
  89. package/src/pages/algo/smart-logs-data.js +0 -218
  90. package/src/pages/algo/smart-logs.js +0 -387
  91. package/src/pages/algo/ui-constants.js +0 -144
  92. package/src/pages/algo/ui-summary.js +0 -184
  93. package/src/pages/stats-calculations.js +0 -191
  94. package/src/pages/stats-ui.js +0 -381
  95. package/src/pages/stats.js +0 -339
  96. package/src/services/ai/client-analysis.js +0 -194
  97. package/src/services/ai/client-models.js +0 -333
  98. package/src/services/ai/client.js +0 -343
  99. package/src/services/ai/index.js +0 -384
  100. package/src/services/ai/oauth-anthropic.js +0 -265
  101. package/src/services/ai/oauth-gemini.js +0 -223
  102. package/src/services/ai/oauth-iflow.js +0 -269
  103. package/src/services/ai/oauth-openai.js +0 -233
  104. package/src/services/ai/oauth-qwen.js +0 -279
  105. package/src/services/ai/providers/index.js +0 -526
  106. package/src/services/ai/proxy-install.js +0 -249
  107. package/src/services/ai/proxy-manager.js +0 -494
  108. package/src/services/ai/proxy-remote.js +0 -161
  109. package/src/services/ai/strategy-supervisor.js +0 -1312
  110. package/src/services/ai/supervisor-data.js +0 -195
  111. package/src/services/ai/supervisor-optimize.js +0 -215
  112. package/src/services/ai/supervisor-sync.js +0 -178
  113. package/src/services/ai/supervisor-utils.js +0 -158
  114. package/src/services/ai/supervisor.js +0 -484
  115. package/src/services/ai/validation.js +0 -250
  116. package/src/services/hqx-server-events.js +0 -110
  117. package/src/services/hqx-server-handlers.js +0 -217
  118. package/src/services/hqx-server-latency.js +0 -136
  119. package/src/services/hqx-server.js +0 -403
  120. package/src/services/position-constants.js +0 -28
  121. package/src/services/position-manager.js +0 -528
  122. package/src/services/position-momentum.js +0 -206
  123. package/src/services/projectx/accounts.js +0 -142
  124. package/src/services/projectx/index.js +0 -443
  125. package/src/services/projectx/market.js +0 -172
  126. package/src/services/projectx/stats.js +0 -110
  127. package/src/services/projectx/trading.js +0 -180
  128. package/src/services/rithmic/latency-tracker.js +0 -182
  129. package/src/services/rithmic/market-data.js +0 -549
  130. package/src/services/rithmic/specs.js +0 -146
  131. package/src/services/rithmic/trade-history.js +0 -254
  132. package/src/services/session-history.js +0 -475
  133. package/src/services/strategy/hft-tick.js +0 -507
  134. package/src/services/strategy/recovery-math.js +0 -402
  135. package/src/services/tradovate/constants.js +0 -109
  136. package/src/services/tradovate/index.js +0 -505
  137. package/src/services/tradovate/market.js +0 -47
  138. package/src/services/tradovate/websocket.js +0 -97
@@ -1,58 +0,0 @@
1
- /**
2
- * Algo Trading Utilities
3
- * Shared functions and constants for algo trading
4
- */
5
-
6
- 'use strict';
7
-
8
- /**
9
- * Format price to avoid floating point errors
10
- * @param {number} price - Raw price
11
- * @param {number} tickSize - Tick size (default 0.25)
12
- * @returns {string} - Formatted price string
13
- */
14
- const formatPrice = (price, tickSize = 0.25) => {
15
- if (price === null || price === undefined || isNaN(price)) return '--';
16
- const rounded = Math.round(price / tickSize) * tickSize;
17
- const decimals = tickSize < 1 ? Math.max(0, -Math.floor(Math.log10(tickSize))) : 0;
18
- return rounded.toFixed(decimals);
19
- };
20
-
21
- /**
22
- * Check if service supports fast path (Rithmic direct)
23
- * @param {Object} service - Trading service
24
- * @returns {boolean}
25
- */
26
- const isRithmicFastPath = (service) => {
27
- return typeof service.fastEntry === 'function' &&
28
- typeof service.fastExit === 'function' &&
29
- service.orderConn?.isConnected;
30
- };
31
-
32
- // Maximum symbols for multi-symbol trading
33
- const MAX_MULTI_SYMBOLS = 5;
34
-
35
- // Use HFT tick-based strategy for Rithmic
36
- const USE_HFT_STRATEGY = true;
37
-
38
- // Timeout for async operations
39
- const TIMEOUT_MS = 5000;
40
-
41
- /**
42
- * Wrap promise with timeout
43
- */
44
- const withTimeout = (promise, ms) => {
45
- return Promise.race([
46
- promise,
47
- new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms))
48
- ]);
49
- };
50
-
51
- module.exports = {
52
- formatPrice,
53
- isRithmicFastPath,
54
- MAX_MULTI_SYMBOLS,
55
- USE_HFT_STRATEGY,
56
- TIMEOUT_MS,
57
- withTimeout
58
- };
@@ -1,449 +0,0 @@
1
- /**
2
- * @fileoverview Copy Trading Engine
3
- * @module pages/algo/copy-engine
4
- *
5
- * Ultra-low latency copy trading with:
6
- * - Fast polling (250ms adaptive)
7
- * - Multi-follower support
8
- * - Parallel order execution
9
- * - Position reconciliation
10
- */
11
-
12
- const { logger } = require('../../utils');
13
- const { algoLogger } = require('./logger');
14
- const StrategySupervisor = require('../../services/ai/strategy-supervisor');
15
-
16
- const log = logger.scope('CopyEngine');
17
-
18
- /**
19
- * CopyEngine - Handles all copy trading logic with professional execution
20
- */
21
- class CopyEngine {
22
- constructor(config) {
23
- this.lead = config.lead;
24
- this.followers = config.followers;
25
- this.symbol = config.symbol;
26
- this.dailyTarget = config.dailyTarget;
27
- this.maxRisk = config.maxRisk;
28
- this.ui = config.ui;
29
- this.stats = config.stats;
30
-
31
- // Engine state
32
- this.running = false;
33
- this.stopReason = null;
34
-
35
- // Position tracking
36
- this.leadPositions = new Map();
37
- this.followerPositions = new Map();
38
- this.pendingOrders = new Map();
39
-
40
- // Order queue
41
- this.orderQueues = new Map();
42
- this.processingQueue = new Map();
43
-
44
- // Timing
45
- this.pollInterval = 250;
46
- this.lastPollTime = 0;
47
- this.pollCount = 0;
48
- this.orderCount = 0;
49
- this.failedOrders = 0;
50
- this.lastLogTime = 0;
51
- this.positionEntryTime = null;
52
-
53
- // Retry configuration
54
- this.maxRetries = 3;
55
- this.retryDelayBase = 100;
56
- this.maxSlippageTicks = 4;
57
- }
58
-
59
- getPositionKey(position) {
60
- return position.contractId || position.symbol || position.id;
61
- }
62
-
63
- resolveSymbol(position, targetAccount) {
64
- const targetType = targetAccount.type;
65
-
66
- if (targetType === 'rithmic') {
67
- return {
68
- symbol: position.symbol || this.symbol.name,
69
- exchange: position.exchange || this.symbol.exchange || 'CME',
70
- contractId: null
71
- };
72
- } else {
73
- return {
74
- contractId: position.contractId || this.symbol.id || this.symbol.contractId,
75
- symbol: null,
76
- exchange: null
77
- };
78
- }
79
- }
80
-
81
- buildOrderData(params, platformType) {
82
- const { accountId, contractId, symbol, exchange, side, size, type, price } = params;
83
-
84
- if (platformType === 'rithmic') {
85
- return { accountId, symbol, exchange: exchange || 'CME', size, side, type, price: price || 0 };
86
- } else {
87
- return { accountId, contractId, type, side, size };
88
- }
89
- }
90
-
91
- async executeOrderWithRetry(follower, orderData, retryCount = 0) {
92
- try {
93
- const startTime = Date.now();
94
- const result = await follower.service.placeOrder(orderData);
95
- const latency = Date.now() - startTime;
96
-
97
- if (result.success) {
98
- this.orderCount++;
99
- this.stats.latency = Math.round((this.stats.latency + latency) / 2);
100
- return { success: true, latency };
101
- }
102
-
103
- if (retryCount < this.maxRetries) {
104
- const delay = this.retryDelayBase * Math.pow(2, retryCount);
105
- await this.sleep(delay);
106
- return this.executeOrderWithRetry(follower, orderData, retryCount + 1);
107
- }
108
-
109
- this.failedOrders++;
110
- return { success: false, error: result.error || 'Max retries exceeded' };
111
- } catch (err) {
112
- if (retryCount < this.maxRetries) {
113
- const delay = this.retryDelayBase * Math.pow(2, retryCount);
114
- await this.sleep(delay);
115
- return this.executeOrderWithRetry(follower, orderData, retryCount + 1);
116
- }
117
-
118
- this.failedOrders++;
119
- return { success: false, error: err.message };
120
- }
121
- }
122
-
123
- async queueOrder(followerIdx, orderFn) {
124
- if (!this.orderQueues.has(followerIdx)) {
125
- this.orderQueues.set(followerIdx, []);
126
- }
127
-
128
- return new Promise((resolve) => {
129
- this.orderQueues.get(followerIdx).push({ fn: orderFn, resolve });
130
- this.processQueue(followerIdx);
131
- });
132
- }
133
-
134
- async processQueue(followerIdx) {
135
- if (this.processingQueue.get(followerIdx)) return;
136
-
137
- const queue = this.orderQueues.get(followerIdx);
138
- if (!queue || queue.length === 0) return;
139
-
140
- this.processingQueue.set(followerIdx, true);
141
-
142
- while (queue.length > 0 && this.running) {
143
- const { fn, resolve } = queue.shift();
144
- try {
145
- const result = await fn();
146
- resolve(result);
147
- } catch (err) {
148
- resolve({ success: false, error: err.message });
149
- }
150
- }
151
-
152
- this.processingQueue.set(followerIdx, false);
153
- }
154
-
155
- async copyPositionOpen(position) {
156
- const side = position.quantity > 0 ? 'LONG' : 'SHORT';
157
- const orderSide = position.quantity > 0 ? 0 : 1;
158
- const displaySymbol = position.symbol || this.symbol.name;
159
- const size = Math.abs(position.quantity);
160
- const entry = position.averagePrice || 0;
161
-
162
- this.positionEntryTime = Date.now();
163
- algoLogger.positionOpened(this.ui, displaySymbol, side, size, entry);
164
-
165
- if (this.stats.aiSupervision) {
166
- StrategySupervisor.feedSignal({
167
- direction: side.toLowerCase(),
168
- entry,
169
- stopLoss: null,
170
- takeProfit: null,
171
- confidence: 0.5
172
- });
173
- }
174
-
175
- const promises = this.followers.map((follower, idx) => {
176
- return this.queueOrder(idx, async () => {
177
- const resolved = this.resolveSymbol(position, follower);
178
- const orderData = this.buildOrderData({
179
- accountId: follower.account.accountId,
180
- contractId: resolved.contractId,
181
- symbol: resolved.symbol,
182
- exchange: resolved.exchange,
183
- side: orderSide,
184
- size: follower.contracts,
185
- type: 2
186
- }, follower.type);
187
-
188
- algoLogger.info(this.ui, 'COPY ORDER', `${side} ${follower.contracts}x -> ${follower.propfirm}`);
189
-
190
- const result = await this.executeOrderWithRetry(follower, orderData);
191
-
192
- if (result.success) {
193
- algoLogger.orderFilled(this.ui, displaySymbol, side, follower.contracts, entry);
194
- const posKey = this.getPositionKey(position);
195
- this.followerPositions.set(`${idx}:${posKey}`, {
196
- ...position,
197
- followerIdx: idx,
198
- openTime: Date.now()
199
- });
200
- } else {
201
- algoLogger.orderRejected(this.ui, displaySymbol, result.error);
202
- }
203
-
204
- return result;
205
- });
206
- });
207
-
208
- const results = await Promise.all(promises);
209
- const successCount = results.filter(r => r.success).length;
210
-
211
- if (successCount === this.followers.length) {
212
- algoLogger.info(this.ui, 'ALL COPIED', `${successCount}/${this.followers.length} followers`);
213
- } else if (successCount > 0) {
214
- algoLogger.info(this.ui, 'PARTIAL COPY', `${successCount}/${this.followers.length} followers`);
215
- }
216
-
217
- return results;
218
- }
219
-
220
- async copyPositionClose(position, exitPrice, pnl) {
221
- const side = position.quantity > 0 ? 'LONG' : 'SHORT';
222
- const closeSide = position.quantity > 0 ? 1 : 0;
223
- const displaySymbol = position.symbol || this.symbol.name;
224
- const size = Math.abs(position.quantity);
225
-
226
- this.positionEntryTime = null;
227
- algoLogger.positionClosed(this.ui, displaySymbol, side, size, exitPrice, pnl);
228
-
229
- if (this.stats.aiSupervision) {
230
- StrategySupervisor.feedTradeResult({
231
- side,
232
- qty: size,
233
- price: exitPrice,
234
- pnl,
235
- symbol: displaySymbol,
236
- direction: side
237
- });
238
-
239
- const aiStatus = StrategySupervisor.getStatus();
240
- if (aiStatus.patternsLearned.winning + aiStatus.patternsLearned.losing > 0) {
241
- algoLogger.info(this.ui, 'AI LEARNING',
242
- `${aiStatus.patternsLearned.winning}W/${aiStatus.patternsLearned.losing}L patterns`);
243
- }
244
- }
245
-
246
- const posKey = this.getPositionKey(position);
247
-
248
- const promises = this.followers.map((follower, idx) => {
249
- return this.queueOrder(idx, async () => {
250
- const resolved = this.resolveSymbol(position, follower);
251
- const posIdentifier = follower.type === 'rithmic'
252
- ? (position.symbol || this.symbol.name)
253
- : (position.contractId || this.symbol.id);
254
-
255
- algoLogger.info(this.ui, 'CLOSE ORDER', `${displaySymbol} -> ${follower.propfirm}`);
256
-
257
- let result = await follower.service.closePosition(follower.account.accountId, posIdentifier);
258
-
259
- if (!result.success) {
260
- const orderData = this.buildOrderData({
261
- accountId: follower.account.accountId,
262
- contractId: resolved.contractId,
263
- symbol: resolved.symbol,
264
- exchange: resolved.exchange,
265
- side: closeSide,
266
- size: follower.contracts,
267
- type: 2
268
- }, follower.type);
269
-
270
- result = await this.executeOrderWithRetry(follower, orderData);
271
- }
272
-
273
- if (result.success) {
274
- algoLogger.info(this.ui, 'CLOSED', `${displaySymbol} on ${follower.propfirm}`);
275
- this.followerPositions.delete(`${idx}:${posKey}`);
276
- } else {
277
- algoLogger.error(this.ui, 'CLOSE FAILED', `${follower.propfirm}: ${result.error}`);
278
- }
279
-
280
- return result;
281
- });
282
- });
283
-
284
- const results = await Promise.all(promises);
285
- const successCount = results.filter(r => r.success).length;
286
-
287
- if (successCount === this.followers.length) {
288
- this.stats.trades++;
289
- this.stats.sessionPnl += pnl;
290
- if (pnl >= 0) this.stats.wins++;
291
- else this.stats.losses++;
292
- }
293
-
294
- return results;
295
- }
296
-
297
- async pollLeadPositions() {
298
- if (!this.running) return;
299
-
300
- const startTime = Date.now();
301
-
302
- try {
303
- const result = await this.lead.service.getPositions(this.lead.account.accountId);
304
- if (!result.success) return;
305
-
306
- const currentPositions = result.positions || [];
307
- const currentMap = new Map();
308
-
309
- for (const pos of currentPositions) {
310
- if (pos.quantity === 0) continue;
311
- const key = this.getPositionKey(pos);
312
- currentMap.set(key, pos);
313
- }
314
-
315
- // Detect new positions
316
- for (const [key, pos] of currentMap) {
317
- if (!this.leadPositions.has(key)) {
318
- await this.copyPositionOpen(pos);
319
- this.leadPositions.set(key, pos);
320
- } else {
321
- const oldPos = this.leadPositions.get(key);
322
- if (Math.abs(pos.quantity) !== Math.abs(oldPos.quantity)) {
323
- this.leadPositions.set(key, pos);
324
- }
325
- }
326
- }
327
-
328
- // Detect closed positions
329
- for (const [key, oldPos] of this.leadPositions) {
330
- if (!currentMap.has(key)) {
331
- const exitPrice = oldPos.averagePrice || 0;
332
- const pnl = oldPos.profitAndLoss || 0;
333
- await this.copyPositionClose(oldPos, exitPrice, pnl);
334
- this.leadPositions.delete(key);
335
- }
336
- }
337
-
338
- // Update P&L
339
- const totalPnL = currentPositions.reduce((sum, p) => sum + (p.profitAndLoss || 0), 0);
340
- this.stats.pnl = totalPnL;
341
-
342
- // Check limits
343
- if (totalPnL >= this.dailyTarget) {
344
- this.stop('target');
345
- algoLogger.info(this.ui, 'TARGET REACHED', `+$${totalPnL.toFixed(2)}`);
346
- } else if (totalPnL <= -this.maxRisk) {
347
- this.stop('risk');
348
- algoLogger.error(this.ui, 'MAX RISK HIT', `-$${Math.abs(totalPnL).toFixed(2)}`);
349
- }
350
-
351
- // Adaptive polling
352
- const pollTime = Date.now() - startTime;
353
- this.stats.latency = pollTime;
354
-
355
- if (this.leadPositions.size > 0) {
356
- this.pollInterval = Math.max(100, Math.min(250, pollTime * 2));
357
- } else {
358
- this.pollInterval = Math.max(250, Math.min(500, pollTime * 3));
359
- }
360
-
361
- this.pollCount++;
362
-
363
- // Smart logs
364
- const now = Date.now();
365
- if (now - this.lastLogTime > 1000) {
366
- const smartLogs = require('./smart-logs');
367
-
368
- if (this.leadPositions.size === 0) {
369
- const scanLog = smartLogs.getScanningLog(true);
370
- this.ui.addLog('info', `${scanLog.message} poll #${this.pollCount} | ${pollTime}ms`);
371
- }
372
- this.lastLogTime = now;
373
- }
374
-
375
- } catch (err) {
376
- log.warn('Poll error', { error: err.message });
377
- }
378
- }
379
-
380
- async reconcilePositions() {
381
- for (let idx = 0; idx < this.followers.length; idx++) {
382
- const follower = this.followers[idx];
383
-
384
- try {
385
- const result = await follower.service.getPositions(follower.account.accountId);
386
- if (!result.success) continue;
387
-
388
- const followerPositions = result.positions || [];
389
-
390
- // Check missing positions
391
- for (const [key, leadPos] of this.leadPositions) {
392
- const hasFollowerPos = followerPositions.some(fp => {
393
- const fpKey = this.getPositionKey(fp);
394
- return fpKey === key && fp.quantity !== 0;
395
- });
396
-
397
- if (!hasFollowerPos) {
398
- algoLogger.info(this.ui, 'RECONCILE', `Missing ${key} on ${follower.propfirm}`);
399
- await this.copyPositionOpen(leadPos);
400
- }
401
- }
402
-
403
- // Check orphaned positions
404
- for (const fp of followerPositions) {
405
- if (fp.quantity === 0) continue;
406
- const fpKey = this.getPositionKey(fp);
407
-
408
- if (!this.leadPositions.has(fpKey)) {
409
- algoLogger.info(this.ui, 'RECONCILE', `Orphaned ${fpKey} on ${follower.propfirm}`);
410
- const posIdentifier = follower.type === 'rithmic' ? fp.symbol : fp.contractId;
411
- await follower.service.closePosition(follower.account.accountId, posIdentifier);
412
- }
413
- }
414
-
415
- } catch (err) {
416
- log.warn('Reconcile error', { follower: follower.propfirm, error: err.message });
417
- }
418
- }
419
- }
420
-
421
- async start() {
422
- this.running = true;
423
- this.stats.connected = true;
424
-
425
- algoLogger.info(this.ui, 'ENGINE STARTED', `Polling every ${this.pollInterval}ms`);
426
- algoLogger.info(this.ui, 'FOLLOWERS', `${this.followers.length} account(s)`);
427
-
428
- await this.reconcilePositions();
429
-
430
- while (this.running) {
431
- await this.pollLeadPositions();
432
- await this.sleep(this.pollInterval);
433
- }
434
-
435
- return this.stopReason;
436
- }
437
-
438
- stop(reason = 'manual') {
439
- this.running = false;
440
- this.stopReason = reason;
441
- this.stats.connected = false;
442
- }
443
-
444
- sleep(ms) {
445
- return new Promise(resolve => setTimeout(resolve, ms));
446
- }
447
- }
448
-
449
- module.exports = { CopyEngine };