hedgequantx 2.5.15 ā 2.5.16
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/menus/dashboard.js +3 -0
- package/src/pages/algo/copy-trading.js +54 -2
- package/src/pages/algo/index.js +1 -1
- package/src/pages/stats.js +52 -0
- package/src/services/ai/client.js +281 -0
- package/src/services/ai/index.js +2 -29
- package/src/services/ai/supervisor.js +337 -485
package/package.json
CHANGED
package/src/menus/dashboard.js
CHANGED
|
@@ -22,6 +22,9 @@ const dashboardMenu = async (service) => {
|
|
|
22
22
|
const boxWidth = getLogoWidth();
|
|
23
23
|
const W = boxWidth - 2;
|
|
24
24
|
|
|
25
|
+
// Check AI connection status
|
|
26
|
+
const aiConnected = aiService.isConnected();
|
|
27
|
+
|
|
25
28
|
const makeLine = (content, align = 'left') => {
|
|
26
29
|
const plainLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
27
30
|
const padding = W - plainLen;
|
|
@@ -330,12 +330,33 @@ const launchCopyTrading = async (config) => {
|
|
|
330
330
|
if (!existing && pos.quantity !== 0) {
|
|
331
331
|
// New position opened - copy to follower
|
|
332
332
|
const side = pos.quantity > 0 ? 'LONG' : 'SHORT';
|
|
333
|
+
const orderSide = pos.quantity > 0 ? 0 : 1; // 0=Buy, 1=Sell
|
|
333
334
|
const symbol = pos.symbol || pos.contractId;
|
|
334
335
|
const size = Math.abs(pos.quantity);
|
|
335
336
|
const entry = pos.averagePrice || 0;
|
|
337
|
+
|
|
336
338
|
algoLogger.positionOpened(ui, symbol, side, size, entry);
|
|
337
339
|
algoLogger.info(ui, 'COPYING TO FOLLOWER', `${side} ${size}x ${symbol}`);
|
|
338
|
-
|
|
340
|
+
|
|
341
|
+
// Place order on follower account
|
|
342
|
+
try {
|
|
343
|
+
const orderResult = await follower.service.placeOrder({
|
|
344
|
+
accountId: follower.account.accountId,
|
|
345
|
+
contractId: pos.contractId,
|
|
346
|
+
type: 2, // Market order
|
|
347
|
+
side: orderSide,
|
|
348
|
+
size: follower.contracts
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
if (orderResult.success) {
|
|
352
|
+
algoLogger.orderFilled(ui, symbol, side, follower.contracts, entry);
|
|
353
|
+
algoLogger.info(ui, 'FOLLOWER ORDER', `${side} ${follower.contracts}x filled`);
|
|
354
|
+
} else {
|
|
355
|
+
algoLogger.orderRejected(ui, symbol, orderResult.error || 'Order failed');
|
|
356
|
+
}
|
|
357
|
+
} catch (err) {
|
|
358
|
+
algoLogger.error(ui, 'FOLLOWER ORDER FAILED', err.message);
|
|
359
|
+
}
|
|
339
360
|
}
|
|
340
361
|
}
|
|
341
362
|
|
|
@@ -344,13 +365,44 @@ const launchCopyTrading = async (config) => {
|
|
|
344
365
|
const stillOpen = currentPositions.find(p => p.contractId === oldPos.contractId);
|
|
345
366
|
if (!stillOpen || stillOpen.quantity === 0) {
|
|
346
367
|
const side = oldPos.quantity > 0 ? 'LONG' : 'SHORT';
|
|
368
|
+
const closeSide = oldPos.quantity > 0 ? 1 : 0; // Opposite side to close
|
|
347
369
|
const symbol = oldPos.symbol || oldPos.contractId;
|
|
348
370
|
const size = Math.abs(oldPos.quantity);
|
|
349
371
|
const exit = stillOpen?.averagePrice || oldPos.averagePrice || 0;
|
|
350
372
|
const pnl = oldPos.profitAndLoss || 0;
|
|
373
|
+
|
|
351
374
|
algoLogger.positionClosed(ui, symbol, side, size, exit, pnl);
|
|
352
375
|
algoLogger.info(ui, 'CLOSING ON FOLLOWER', symbol);
|
|
353
|
-
|
|
376
|
+
|
|
377
|
+
// Close position on follower account
|
|
378
|
+
try {
|
|
379
|
+
// First try closePosition API
|
|
380
|
+
const closeResult = await follower.service.closePosition(
|
|
381
|
+
follower.account.accountId,
|
|
382
|
+
oldPos.contractId
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
if (closeResult.success) {
|
|
386
|
+
algoLogger.info(ui, 'FOLLOWER CLOSED', `${symbol} position closed`);
|
|
387
|
+
} else {
|
|
388
|
+
// Fallback: place market order to close
|
|
389
|
+
const orderResult = await follower.service.placeOrder({
|
|
390
|
+
accountId: follower.account.accountId,
|
|
391
|
+
contractId: oldPos.contractId,
|
|
392
|
+
type: 2, // Market order
|
|
393
|
+
side: closeSide,
|
|
394
|
+
size: follower.contracts
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
if (orderResult.success) {
|
|
398
|
+
algoLogger.info(ui, 'FOLLOWER CLOSED', `${symbol} via market order`);
|
|
399
|
+
} else {
|
|
400
|
+
algoLogger.error(ui, 'FOLLOWER CLOSE FAILED', orderResult.error || 'Close failed');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} catch (err) {
|
|
404
|
+
algoLogger.error(ui, 'FOLLOWER CLOSE ERROR', err.message);
|
|
405
|
+
}
|
|
354
406
|
}
|
|
355
407
|
}
|
|
356
408
|
|
package/src/pages/algo/index.js
CHANGED
|
@@ -182,7 +182,7 @@ const startAISupervised = async (service, agent) => {
|
|
|
182
182
|
|
|
183
183
|
await prompts.waitForEnter();
|
|
184
184
|
|
|
185
|
-
//
|
|
185
|
+
// Launch algo trading with AI supervision active
|
|
186
186
|
return await oneAccountMenu(service);
|
|
187
187
|
|
|
188
188
|
} else {
|
package/src/pages/stats.js
CHANGED
|
@@ -14,6 +14,8 @@ const asciichart = require('asciichart');
|
|
|
14
14
|
const { connections } = require('../services');
|
|
15
15
|
const { getLogoWidth, visibleLength, drawBoxHeader, drawBoxFooter, getColWidths, draw2ColHeader, draw2ColSeparator, fmtRow } = require('../ui');
|
|
16
16
|
const { prompts } = require('../utils');
|
|
17
|
+
const aiService = require('../services/ai');
|
|
18
|
+
const AISupervisor = require('../services/ai/supervisor');
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Show Stats Page
|
|
@@ -383,6 +385,56 @@ const showStats = async (service) => {
|
|
|
383
385
|
|
|
384
386
|
drawBoxFooter(boxWidth);
|
|
385
387
|
|
|
388
|
+
// ========== AI SUPERVISION ==========
|
|
389
|
+
const aiAgents = aiService.getAgents();
|
|
390
|
+
const supervisionStatus = AISupervisor.getAllStatus();
|
|
391
|
+
|
|
392
|
+
if (aiAgents.length > 0) {
|
|
393
|
+
console.log();
|
|
394
|
+
drawBoxHeader('AI SUPERVISION', boxWidth);
|
|
395
|
+
draw2ColHeader('AGENTS', 'PERFORMANCE', boxWidth);
|
|
396
|
+
|
|
397
|
+
// Agent info
|
|
398
|
+
const activeAgent = aiService.getActiveAgent();
|
|
399
|
+
const agentNames = aiAgents.map(a => a.name).join(', ');
|
|
400
|
+
const agentMode = aiAgents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
|
|
401
|
+
const modeColor = aiAgents.length >= 2 ? chalk.magenta : chalk.cyan;
|
|
402
|
+
|
|
403
|
+
// Supervision metrics
|
|
404
|
+
let totalDecisions = 0;
|
|
405
|
+
let totalInterventions = 0;
|
|
406
|
+
let totalOptimizations = 0;
|
|
407
|
+
let totalRiskWarnings = 0;
|
|
408
|
+
let totalSessionTime = 0;
|
|
409
|
+
|
|
410
|
+
for (const status of supervisionStatus) {
|
|
411
|
+
if (status.active) {
|
|
412
|
+
totalDecisions += status.metrics?.totalDecisions || 0;
|
|
413
|
+
totalInterventions += status.metrics?.interventions || 0;
|
|
414
|
+
totalOptimizations += status.metrics?.optimizations || 0;
|
|
415
|
+
totalRiskWarnings += status.metrics?.riskWarnings || 0;
|
|
416
|
+
totalSessionTime += status.duration || 0;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const sessionTimeStr = totalSessionTime > 0
|
|
421
|
+
? Math.floor(totalSessionTime / 60000) + 'm ' + Math.floor((totalSessionTime % 60000) / 1000) + 's'
|
|
422
|
+
: 'INACTIVE';
|
|
423
|
+
|
|
424
|
+
// Get real supervision data
|
|
425
|
+
const supervisionData = AISupervisor.getAggregatedData();
|
|
426
|
+
const supervisedAccounts = supervisionData.totalAccounts;
|
|
427
|
+
const supervisedPnL = supervisionData.totalPnL;
|
|
428
|
+
|
|
429
|
+
console.log(chalk.cyan('\u2551') + fmtRow('CONNECTED AGENTS:', chalk.green(String(aiAgents.length)), col1) + chalk.cyan('\u2502') + fmtRow('SUPERVISED ACCOUNTS:', supervisedAccounts > 0 ? chalk.white(String(supervisedAccounts)) : chalk.gray('0'), col2) + chalk.cyan('\u2551'));
|
|
430
|
+
console.log(chalk.cyan('\u2551') + fmtRow('MODE:', modeColor(agentMode), col1) + chalk.cyan('\u2502') + fmtRow('SUPERVISED P&L:', supervisedPnL !== 0 ? (supervisedPnL >= 0 ? chalk.green('$' + supervisedPnL.toFixed(2)) : chalk.red('$' + supervisedPnL.toFixed(2))) : chalk.gray('$0.00'), col2) + chalk.cyan('\u2551'));
|
|
431
|
+
console.log(chalk.cyan('\u2551') + fmtRow('ACTIVE:', activeAgent ? chalk.green(activeAgent.name) : chalk.gray('NONE'), col1) + chalk.cyan('\u2502') + fmtRow('POSITIONS:', chalk.white(String(supervisionData.totalPositions)), col2) + chalk.cyan('\u2551'));
|
|
432
|
+
console.log(chalk.cyan('\u2551') + fmtRow('SESSION TIME:', chalk.white(sessionTimeStr), col1) + chalk.cyan('\u2502') + fmtRow('OPEN ORDERS:', chalk.white(String(supervisionData.totalOrders)), col2) + chalk.cyan('\u2551'));
|
|
433
|
+
console.log(chalk.cyan('\u2551') + fmtRow('AGENTS:', chalk.gray(agentNames.substring(0, col1 - 10)), col1) + chalk.cyan('\u2502') + fmtRow('TRADES TODAY:', chalk.white(String(supervisionData.totalTrades)), col2) + chalk.cyan('\u2551'));
|
|
434
|
+
|
|
435
|
+
drawBoxFooter(boxWidth);
|
|
436
|
+
}
|
|
437
|
+
|
|
386
438
|
// ========== EQUITY CURVE ==========
|
|
387
439
|
console.log();
|
|
388
440
|
drawBoxHeader('EQUITY CURVE', boxWidth);
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Client - Makes real API calls to AI providers
|
|
3
|
+
*
|
|
4
|
+
* STRICT RULE: No mock responses. Real API calls only.
|
|
5
|
+
* If API fails ā return null, not fake data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const https = require('https');
|
|
9
|
+
const http = require('http');
|
|
10
|
+
const { getProvider } = require('./providers');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Make HTTP request to AI provider
|
|
14
|
+
* @param {string} url - Full URL
|
|
15
|
+
* @param {Object} options - Request options
|
|
16
|
+
* @returns {Promise<Object>} Response data
|
|
17
|
+
*/
|
|
18
|
+
const makeRequest = (url, options) => {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const parsedUrl = new URL(url);
|
|
21
|
+
const protocol = parsedUrl.protocol === 'https:' ? https : http;
|
|
22
|
+
|
|
23
|
+
const req = protocol.request(url, {
|
|
24
|
+
method: options.method || 'POST',
|
|
25
|
+
headers: options.headers || {},
|
|
26
|
+
timeout: options.timeout || 30000
|
|
27
|
+
}, (res) => {
|
|
28
|
+
let data = '';
|
|
29
|
+
res.on('data', chunk => data += chunk);
|
|
30
|
+
res.on('end', () => {
|
|
31
|
+
try {
|
|
32
|
+
const json = JSON.parse(data);
|
|
33
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
34
|
+
resolve(json);
|
|
35
|
+
} else {
|
|
36
|
+
reject(new Error(json.error?.message || `HTTP ${res.statusCode}`));
|
|
37
|
+
}
|
|
38
|
+
} catch (e) {
|
|
39
|
+
reject(new Error(`Invalid JSON response: ${data.substring(0, 100)}`));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
req.on('error', reject);
|
|
45
|
+
req.on('timeout', () => reject(new Error('Request timeout')));
|
|
46
|
+
|
|
47
|
+
if (options.body) {
|
|
48
|
+
req.write(JSON.stringify(options.body));
|
|
49
|
+
}
|
|
50
|
+
req.end();
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Call OpenAI-compatible API
|
|
56
|
+
* Works with: OpenAI, Groq, Together, DeepSeek, Mistral, xAI, etc.
|
|
57
|
+
* @param {Object} agent - Agent configuration
|
|
58
|
+
* @param {string} prompt - User prompt
|
|
59
|
+
* @param {string} systemPrompt - System prompt
|
|
60
|
+
* @returns {Promise<string|null>} Response text or null on error
|
|
61
|
+
*/
|
|
62
|
+
const callOpenAICompatible = async (agent, prompt, systemPrompt) => {
|
|
63
|
+
const provider = getProvider(agent.providerId);
|
|
64
|
+
if (!provider) return null;
|
|
65
|
+
|
|
66
|
+
const endpoint = agent.credentials?.endpoint || provider.endpoint;
|
|
67
|
+
const apiKey = agent.credentials?.apiKey;
|
|
68
|
+
const model = agent.model || provider.defaultModel;
|
|
69
|
+
|
|
70
|
+
if (!apiKey && provider.category !== 'local') {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const url = `${endpoint}/chat/completions`;
|
|
75
|
+
|
|
76
|
+
const headers = {
|
|
77
|
+
'Content-Type': 'application/json'
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (apiKey) {
|
|
81
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// OpenRouter requires additional headers
|
|
85
|
+
if (agent.providerId === 'openrouter') {
|
|
86
|
+
headers['HTTP-Referer'] = 'https://hedgequantx.com';
|
|
87
|
+
headers['X-Title'] = 'HQX-CLI';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const body = {
|
|
91
|
+
model,
|
|
92
|
+
messages: [
|
|
93
|
+
{ role: 'system', content: systemPrompt },
|
|
94
|
+
{ role: 'user', content: prompt }
|
|
95
|
+
],
|
|
96
|
+
temperature: 0.3,
|
|
97
|
+
max_tokens: 500
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const response = await makeRequest(url, { headers, body, timeout: 30000 });
|
|
102
|
+
return response.choices?.[0]?.message?.content || null;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Call Anthropic Claude API
|
|
110
|
+
* @param {Object} agent - Agent configuration
|
|
111
|
+
* @param {string} prompt - User prompt
|
|
112
|
+
* @param {string} systemPrompt - System prompt
|
|
113
|
+
* @returns {Promise<string|null>} Response text or null on error
|
|
114
|
+
*/
|
|
115
|
+
const callAnthropic = async (agent, prompt, systemPrompt) => {
|
|
116
|
+
const provider = getProvider('anthropic');
|
|
117
|
+
if (!provider) return null;
|
|
118
|
+
|
|
119
|
+
const apiKey = agent.credentials?.apiKey;
|
|
120
|
+
const model = agent.model || provider.defaultModel;
|
|
121
|
+
|
|
122
|
+
if (!apiKey) return null;
|
|
123
|
+
|
|
124
|
+
const url = `${provider.endpoint}/messages`;
|
|
125
|
+
|
|
126
|
+
const headers = {
|
|
127
|
+
'Content-Type': 'application/json',
|
|
128
|
+
'x-api-key': apiKey,
|
|
129
|
+
'anthropic-version': '2023-06-01'
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const body = {
|
|
133
|
+
model,
|
|
134
|
+
max_tokens: 500,
|
|
135
|
+
system: systemPrompt,
|
|
136
|
+
messages: [
|
|
137
|
+
{ role: 'user', content: prompt }
|
|
138
|
+
]
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const response = await makeRequest(url, { headers, body, timeout: 30000 });
|
|
143
|
+
return response.content?.[0]?.text || null;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Call Google Gemini API
|
|
151
|
+
* @param {Object} agent - Agent configuration
|
|
152
|
+
* @param {string} prompt - User prompt
|
|
153
|
+
* @param {string} systemPrompt - System prompt
|
|
154
|
+
* @returns {Promise<string|null>} Response text or null on error
|
|
155
|
+
*/
|
|
156
|
+
const callGemini = async (agent, prompt, systemPrompt) => {
|
|
157
|
+
const provider = getProvider('gemini');
|
|
158
|
+
if (!provider) return null;
|
|
159
|
+
|
|
160
|
+
const apiKey = agent.credentials?.apiKey;
|
|
161
|
+
const model = agent.model || provider.defaultModel;
|
|
162
|
+
|
|
163
|
+
if (!apiKey) return null;
|
|
164
|
+
|
|
165
|
+
const url = `${provider.endpoint}/models/${model}:generateContent?key=${apiKey}`;
|
|
166
|
+
|
|
167
|
+
const headers = {
|
|
168
|
+
'Content-Type': 'application/json'
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const body = {
|
|
172
|
+
contents: [
|
|
173
|
+
{ role: 'user', parts: [{ text: `${systemPrompt}\n\n${prompt}` }] }
|
|
174
|
+
],
|
|
175
|
+
generationConfig: {
|
|
176
|
+
temperature: 0.3,
|
|
177
|
+
maxOutputTokens: 500
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const response = await makeRequest(url, { headers, body, timeout: 30000 });
|
|
183
|
+
return response.candidates?.[0]?.content?.parts?.[0]?.text || null;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Call AI provider based on agent configuration
|
|
191
|
+
* @param {Object} agent - Agent with providerId and credentials
|
|
192
|
+
* @param {string} prompt - User prompt
|
|
193
|
+
* @param {string} systemPrompt - System prompt
|
|
194
|
+
* @returns {Promise<string|null>} AI response or null on error
|
|
195
|
+
*/
|
|
196
|
+
const callAI = async (agent, prompt, systemPrompt = '') => {
|
|
197
|
+
if (!agent || !agent.providerId) return null;
|
|
198
|
+
|
|
199
|
+
switch (agent.providerId) {
|
|
200
|
+
case 'anthropic':
|
|
201
|
+
return callAnthropic(agent, prompt, systemPrompt);
|
|
202
|
+
|
|
203
|
+
case 'gemini':
|
|
204
|
+
return callGemini(agent, prompt, systemPrompt);
|
|
205
|
+
|
|
206
|
+
// All OpenAI-compatible APIs
|
|
207
|
+
case 'openai':
|
|
208
|
+
case 'openrouter':
|
|
209
|
+
case 'deepseek':
|
|
210
|
+
case 'groq':
|
|
211
|
+
case 'xai':
|
|
212
|
+
case 'mistral':
|
|
213
|
+
case 'perplexity':
|
|
214
|
+
case 'together':
|
|
215
|
+
case 'qwen':
|
|
216
|
+
case 'moonshot':
|
|
217
|
+
case 'yi':
|
|
218
|
+
case 'zhipu':
|
|
219
|
+
case 'baichuan':
|
|
220
|
+
case 'ollama':
|
|
221
|
+
case 'lmstudio':
|
|
222
|
+
case 'custom':
|
|
223
|
+
return callOpenAICompatible(agent, prompt, systemPrompt);
|
|
224
|
+
|
|
225
|
+
default:
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Analyze trading data with AI
|
|
232
|
+
* @param {Object} agent - AI agent
|
|
233
|
+
* @param {Object} data - Trading data from APIs
|
|
234
|
+
* @returns {Promise<Object|null>} Analysis result or null
|
|
235
|
+
*/
|
|
236
|
+
const analyzeTrading = async (agent, data) => {
|
|
237
|
+
if (!agent || !data) return null;
|
|
238
|
+
|
|
239
|
+
const systemPrompt = `You are a professional trading analyst for prop firm futures trading.
|
|
240
|
+
Analyze the provided real-time trading data and provide actionable insights.
|
|
241
|
+
Be concise. Focus on risk management and optimization.
|
|
242
|
+
Respond in JSON format with: { "action": "HOLD|REDUCE_SIZE|PAUSE|CONTINUE", "confidence": 0-100, "reason": "brief reason" }`;
|
|
243
|
+
|
|
244
|
+
const prompt = `Current trading session data:
|
|
245
|
+
- Account Balance: ${data.account?.balance ?? 'N/A'}
|
|
246
|
+
- Today P&L: ${data.account?.profitAndLoss ?? 'N/A'}
|
|
247
|
+
- Open Positions: ${data.positions?.length ?? 0}
|
|
248
|
+
- Open Orders: ${data.orders?.length ?? 0}
|
|
249
|
+
- Today Trades: ${data.trades?.length ?? 0}
|
|
250
|
+
|
|
251
|
+
${data.positions?.length > 0 ? `Positions: ${JSON.stringify(data.positions.map(p => ({
|
|
252
|
+
symbol: p.symbol || p.contractId,
|
|
253
|
+
qty: p.quantity,
|
|
254
|
+
pnl: p.profitAndLoss
|
|
255
|
+
})))}` : ''}
|
|
256
|
+
|
|
257
|
+
Analyze and provide recommendation.`;
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const response = await callAI(agent, prompt, systemPrompt);
|
|
261
|
+
if (!response) return null;
|
|
262
|
+
|
|
263
|
+
// Try to parse JSON from response
|
|
264
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
265
|
+
if (jsonMatch) {
|
|
266
|
+
return JSON.parse(jsonMatch[0]);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return null;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
module.exports = {
|
|
276
|
+
callAI,
|
|
277
|
+
analyzeTrading,
|
|
278
|
+
callOpenAICompatible,
|
|
279
|
+
callAnthropic,
|
|
280
|
+
callGemini
|
|
281
|
+
};
|
package/src/services/ai/index.js
CHANGED
|
@@ -214,36 +214,9 @@ const addAgent = async (providerId, optionId, credentials, model = null, customN
|
|
|
214
214
|
|
|
215
215
|
saveAISettings(aiSettings);
|
|
216
216
|
|
|
217
|
-
//
|
|
217
|
+
// Agent is ready - supervision will start when algo trading begins
|
|
218
|
+
// Supervision requires a real service connection and account
|
|
218
219
|
const agent = getAgent(agentId);
|
|
219
|
-
if (agent) {
|
|
220
|
-
// Mock algo target - in real implementation, this would be HQX Ultra Scalping
|
|
221
|
-
const mockAlgo = {
|
|
222
|
-
name: 'HQX Ultra Scalping',
|
|
223
|
-
status: 'ready',
|
|
224
|
-
active: false
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// Check if other agents are already supervising
|
|
228
|
-
const allAgents = getAgents();
|
|
229
|
-
const activeSupervisionCount = allAgents.filter(a => a.id !== agentId && a.isActive).length;
|
|
230
|
-
|
|
231
|
-
if (activeSupervisionCount === 0) {
|
|
232
|
-
// First agent - start single supervision
|
|
233
|
-
AISupervisor.start(agentId, mockAlgo);
|
|
234
|
-
console.log(`\nš¤ AI supervision started for ${agent.name}`);
|
|
235
|
-
console.log(` Monitoring: HQX Ultra Scalping`);
|
|
236
|
-
console.log(` Mode: Single agent supervision`);
|
|
237
|
-
} else {
|
|
238
|
-
// Additional agent - switch to consensus mode
|
|
239
|
-
console.log(`\nš¤ ${agent.name} added to supervision`);
|
|
240
|
-
console.log(` Switching to multi-agent consensus mode`);
|
|
241
|
-
console.log(` Total agents: ${allAgents.length}`);
|
|
242
|
-
|
|
243
|
-
// Start consensus mode would be handled by supervisor
|
|
244
|
-
AISupervisor.start(agentId, mockAlgo);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
220
|
|
|
248
221
|
return agent;
|
|
249
222
|
};
|