hedgequantx 2.6.161 → 2.6.162
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/ai-agent-connect.js +181 -0
- package/src/menus/ai-agent-models.js +219 -0
- package/src/menus/ai-agent-oauth.js +292 -0
- package/src/menus/ai-agent-ui.js +141 -0
- package/src/menus/ai-agent.js +88 -1489
- package/src/pages/algo/copy-engine.js +449 -0
- package/src/pages/algo/copy-trading.js +11 -543
- package/src/pages/algo/smart-logs-data.js +218 -0
- package/src/pages/algo/smart-logs.js +9 -214
- package/src/pages/algo/ui-constants.js +144 -0
- package/src/pages/algo/ui-summary.js +184 -0
- package/src/pages/algo/ui.js +42 -526
- package/src/pages/stats-calculations.js +191 -0
- package/src/pages/stats-ui.js +381 -0
- package/src/pages/stats.js +14 -507
- package/src/services/ai/client-analysis.js +194 -0
- package/src/services/ai/client-models.js +333 -0
- package/src/services/ai/client.js +6 -489
- package/src/services/ai/index.js +2 -257
- package/src/services/ai/proxy-install.js +249 -0
- package/src/services/ai/proxy-manager.js +29 -411
- package/src/services/ai/proxy-remote.js +161 -0
- package/src/services/ai/supervisor-optimize.js +215 -0
- package/src/services/ai/supervisor-sync.js +178 -0
- package/src/services/ai/supervisor.js +50 -515
- package/src/services/ai/validation.js +250 -0
- package/src/services/hqx-server-events.js +110 -0
- package/src/services/hqx-server-handlers.js +217 -0
- package/src/services/hqx-server-latency.js +136 -0
- package/src/services/hqx-server.js +51 -403
- package/src/services/position-constants.js +28 -0
- package/src/services/position-manager.js +105 -554
- package/src/services/position-momentum.js +206 -0
- package/src/services/projectx/accounts.js +142 -0
- package/src/services/projectx/index.js +40 -289
- package/src/services/projectx/trading.js +180 -0
- package/src/services/rithmic/handlers.js +2 -208
- package/src/services/rithmic/index.js +32 -542
- package/src/services/rithmic/latency-tracker.js +182 -0
- package/src/services/rithmic/specs.js +146 -0
- package/src/services/rithmic/trade-history.js +254 -0
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
const https = require('https');
|
|
13
13
|
const http = require('http');
|
|
14
14
|
const { getProvider } = require('./providers');
|
|
15
|
+
const { analyzeTrading: analyzeTradingImpl, analyzePerformance: analyzePerformanceImpl, getMarketAdvice: getMarketAdviceImpl } = require('./client-analysis');
|
|
16
|
+
const { fetchAnthropicModels, fetchAnthropicModelsOAuth, fetchGeminiModels, fetchOpenAIModels, fetchModelsWithOAuth, fetchModelsViaProxy } = require('./client-models');
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Make HTTP request to AI provider
|
|
@@ -317,495 +319,10 @@ const callAI = async (agent, prompt, systemPrompt = '', options = {}) => {
|
|
|
317
319
|
}
|
|
318
320
|
};
|
|
319
321
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
* @returns {Promise<Object|null>} Analysis result or null
|
|
325
|
-
*/
|
|
326
|
-
const analyzeTrading = async (agent, data) => {
|
|
327
|
-
if (!agent || !data) return null;
|
|
328
|
-
|
|
329
|
-
const systemPrompt = `You are a professional trading analyst for prop firm futures trading.
|
|
330
|
-
Analyze the provided real-time trading data and provide actionable insights.
|
|
331
|
-
Be concise. Focus on risk management and optimization.
|
|
332
|
-
Respond in JSON format with: { "action": "HOLD|REDUCE_SIZE|PAUSE|CONTINUE", "confidence": 0-100, "reason": "brief reason" }`;
|
|
333
|
-
|
|
334
|
-
const prompt = `Current trading session data:
|
|
335
|
-
- Account Balance: ${data.account?.balance ?? 'N/A'}
|
|
336
|
-
- Today P&L: ${data.account?.profitAndLoss ?? 'N/A'}
|
|
337
|
-
- Open Positions: ${data.positions?.length ?? 0}
|
|
338
|
-
- Open Orders: ${data.orders?.length ?? 0}
|
|
339
|
-
- Today Trades: ${data.trades?.length ?? 0}
|
|
340
|
-
|
|
341
|
-
${data.positions?.length > 0 ? `Positions: ${JSON.stringify(data.positions.map(p => ({
|
|
342
|
-
symbol: p.symbol || p.contractId,
|
|
343
|
-
qty: p.quantity,
|
|
344
|
-
pnl: p.profitAndLoss
|
|
345
|
-
})))}` : ''}
|
|
346
|
-
|
|
347
|
-
Analyze and provide recommendation.`;
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
const response = await callAI(agent, prompt, systemPrompt);
|
|
351
|
-
if (!response) return null;
|
|
352
|
-
|
|
353
|
-
// Try to parse JSON from response
|
|
354
|
-
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
355
|
-
if (jsonMatch) {
|
|
356
|
-
return JSON.parse(jsonMatch[0]);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return null;
|
|
360
|
-
} catch (error) {
|
|
361
|
-
return null;
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Analyze strategy performance and suggest optimizations
|
|
367
|
-
* Called periodically to help the strategy improve
|
|
368
|
-
*
|
|
369
|
-
* @param {Object} agent - AI agent
|
|
370
|
-
* @param {Object} performanceData - Strategy performance data
|
|
371
|
-
* @returns {Promise<Object|null>} Optimization suggestions
|
|
372
|
-
*/
|
|
373
|
-
const analyzePerformance = async (agent, performanceData) => {
|
|
374
|
-
if (!agent || !performanceData) return null;
|
|
375
|
-
|
|
376
|
-
const systemPrompt = `You are an AI supervisor for HQX Ultra Scalping, a professional prop firm futures trading strategy.
|
|
377
|
-
|
|
378
|
-
The strategy uses advanced mathematical models:
|
|
379
|
-
- Order flow analysis (delta, cumulative delta, absorption)
|
|
380
|
-
- Market microstructure (bid/ask imbalance, volume profile)
|
|
381
|
-
- Statistical edge detection (z-score, standard deviation bands)
|
|
382
|
-
- Dynamic risk management (Kelly criterion, volatility-adjusted sizing)
|
|
383
|
-
|
|
384
|
-
Your job is to analyze performance data and suggest parameter optimizations.
|
|
385
|
-
Be precise and actionable. Focus on improving win rate, reducing drawdown, and optimizing risk/reward.
|
|
386
|
-
|
|
387
|
-
Respond ONLY in valid JSON format:
|
|
388
|
-
{
|
|
389
|
-
"assessment": "brief performance assessment",
|
|
390
|
-
"winRateAnalysis": "analysis of win/loss patterns",
|
|
391
|
-
"riskAnalysis": "analysis of risk management",
|
|
392
|
-
"optimizations": [
|
|
393
|
-
{ "param": "parameter_name", "current": "current_value", "suggested": "new_value", "reason": "why" }
|
|
394
|
-
],
|
|
395
|
-
"marketCondition": "trending|ranging|volatile|calm",
|
|
396
|
-
"confidence": 0-100
|
|
397
|
-
}`;
|
|
398
|
-
|
|
399
|
-
const prompt = `STRATEGY PERFORMANCE DATA - ANALYZE AND OPTIMIZE
|
|
400
|
-
|
|
401
|
-
Session Stats:
|
|
402
|
-
- Trades: ${performanceData.trades || 0}
|
|
403
|
-
- Wins: ${performanceData.wins || 0}
|
|
404
|
-
- Losses: ${performanceData.losses || 0}
|
|
405
|
-
- Win Rate: ${performanceData.winRate ? (performanceData.winRate * 100).toFixed(1) + '%' : 'N/A'}
|
|
406
|
-
- Total P&L: $${performanceData.pnl?.toFixed(2) || '0.00'}
|
|
407
|
-
- Avg Win: $${performanceData.avgWin?.toFixed(2) || 'N/A'}
|
|
408
|
-
- Avg Loss: $${performanceData.avgLoss?.toFixed(2) || 'N/A'}
|
|
409
|
-
- Largest Win: $${performanceData.largestWin?.toFixed(2) || 'N/A'}
|
|
410
|
-
- Largest Loss: $${performanceData.largestLoss?.toFixed(2) || 'N/A'}
|
|
411
|
-
- Max Drawdown: $${performanceData.maxDrawdown?.toFixed(2) || 'N/A'}
|
|
412
|
-
- Profit Factor: ${performanceData.profitFactor?.toFixed(2) || 'N/A'}
|
|
413
|
-
|
|
414
|
-
Current Parameters:
|
|
415
|
-
- Position Size: ${performanceData.positionSize || 'N/A'} contracts
|
|
416
|
-
- Daily Target: $${performanceData.dailyTarget || 'N/A'}
|
|
417
|
-
- Max Risk: $${performanceData.maxRisk || 'N/A'}
|
|
418
|
-
- Symbol: ${performanceData.symbol || 'N/A'}
|
|
419
|
-
|
|
420
|
-
Recent Trades:
|
|
421
|
-
${performanceData.recentTrades?.map(t =>
|
|
422
|
-
`- ${t.side} ${t.qty}x @ ${t.price} → P&L: $${t.pnl?.toFixed(2) || 'N/A'}`
|
|
423
|
-
).join('\n') || 'No recent trades'}
|
|
424
|
-
|
|
425
|
-
Market Context:
|
|
426
|
-
- Volatility: ${performanceData.volatility || 'N/A'}
|
|
427
|
-
- Trend: ${performanceData.trend || 'N/A'}
|
|
428
|
-
- Session: ${performanceData.session || 'N/A'}
|
|
429
|
-
|
|
430
|
-
Analyze and suggest optimizations to improve performance.`;
|
|
431
|
-
|
|
432
|
-
try {
|
|
433
|
-
const response = await callAI(agent, prompt, systemPrompt);
|
|
434
|
-
if (!response) return null;
|
|
435
|
-
|
|
436
|
-
// Parse JSON from response
|
|
437
|
-
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
438
|
-
if (jsonMatch) {
|
|
439
|
-
return JSON.parse(jsonMatch[0]);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return null;
|
|
443
|
-
} catch (error) {
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Get real-time trading advice based on current market conditions
|
|
450
|
-
*
|
|
451
|
-
* @param {Object} agent - AI agent
|
|
452
|
-
* @param {Object} marketData - Current market data
|
|
453
|
-
* @returns {Promise<Object|null>} Trading advice
|
|
454
|
-
*/
|
|
455
|
-
const getMarketAdvice = async (agent, marketData) => {
|
|
456
|
-
if (!agent || !marketData) return null;
|
|
457
|
-
|
|
458
|
-
const systemPrompt = `You are an AI supervisor for HQX Ultra Scalping futures strategy.
|
|
459
|
-
Analyze real-time market data and provide actionable advice.
|
|
460
|
-
Be concise and precise. The strategy will use your recommendations.
|
|
461
|
-
|
|
462
|
-
Respond ONLY in valid JSON:
|
|
463
|
-
{
|
|
464
|
-
"action": "AGGRESSIVE|NORMAL|CAUTIOUS|PAUSE",
|
|
465
|
-
"sizeMultiplier": 0.5-1.5,
|
|
466
|
-
"reason": "brief reason",
|
|
467
|
-
"confidence": 0-100
|
|
468
|
-
}`;
|
|
469
|
-
|
|
470
|
-
const prompt = `REAL-TIME MARKET ANALYSIS
|
|
471
|
-
|
|
472
|
-
Current Price: ${marketData.price || 'N/A'}
|
|
473
|
-
Bid: ${marketData.bid || 'N/A'} | Ask: ${marketData.ask || 'N/A'}
|
|
474
|
-
Spread: ${marketData.spread || 'N/A'}
|
|
475
|
-
Volume: ${marketData.volume || 'N/A'}
|
|
476
|
-
Delta: ${marketData.delta || 'N/A'}
|
|
477
|
-
Volatility: ${marketData.volatility || 'N/A'}
|
|
478
|
-
|
|
479
|
-
Recent Price Action:
|
|
480
|
-
- High: ${marketData.high || 'N/A'}
|
|
481
|
-
- Low: ${marketData.low || 'N/A'}
|
|
482
|
-
- Range: ${marketData.range || 'N/A'}
|
|
483
|
-
|
|
484
|
-
Current Position: ${marketData.position || 'FLAT'}
|
|
485
|
-
Session P&L: $${marketData.pnl?.toFixed(2) || '0.00'}
|
|
486
|
-
|
|
487
|
-
What should the strategy do?`;
|
|
488
|
-
|
|
489
|
-
try {
|
|
490
|
-
const response = await callAI(agent, prompt, systemPrompt);
|
|
491
|
-
if (!response) return null;
|
|
492
|
-
|
|
493
|
-
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
494
|
-
if (jsonMatch) {
|
|
495
|
-
return JSON.parse(jsonMatch[0]);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
return null;
|
|
499
|
-
} catch (error) {
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Fetch available models from Anthropic API (API Key auth)
|
|
506
|
-
* @param {string} apiKey - API key
|
|
507
|
-
* @returns {Promise<Array|null>} Array of model IDs or null on error
|
|
508
|
-
*
|
|
509
|
-
* Data source: https://api.anthropic.com/v1/models (GET)
|
|
510
|
-
*/
|
|
511
|
-
const fetchAnthropicModels = async (apiKey) => {
|
|
512
|
-
if (!apiKey) return null;
|
|
513
|
-
|
|
514
|
-
const url = 'https://api.anthropic.com/v1/models';
|
|
515
|
-
|
|
516
|
-
const headers = {
|
|
517
|
-
'x-api-key': apiKey,
|
|
518
|
-
'anthropic-version': '2023-06-01'
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
try {
|
|
522
|
-
const response = await makeRequest(url, { method: 'GET', headers, timeout: 10000 });
|
|
523
|
-
if (response.data && Array.isArray(response.data)) {
|
|
524
|
-
return response.data.map(m => m.id).filter(Boolean);
|
|
525
|
-
}
|
|
526
|
-
return null;
|
|
527
|
-
} catch (error) {
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Fetch available models from Anthropic API (OAuth auth)
|
|
534
|
-
*
|
|
535
|
-
* @param {string} accessToken - OAuth access token
|
|
536
|
-
* @returns {Promise<Object>} { models: Array, error: string|null }
|
|
537
|
-
*
|
|
538
|
-
* Data source: https://api.anthropic.com/v1/models (GET with Bearer token)
|
|
539
|
-
* NO HARDCODED FALLBACK - models must come from API only
|
|
540
|
-
*/
|
|
541
|
-
const fetchAnthropicModelsOAuth = async (accessToken) => {
|
|
542
|
-
if (!accessToken) return { models: null, error: 'No access token provided' };
|
|
543
|
-
|
|
544
|
-
const modelsUrl = 'https://api.anthropic.com/v1/models';
|
|
545
|
-
|
|
546
|
-
const headers = {
|
|
547
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
548
|
-
'anthropic-version': '2023-06-01',
|
|
549
|
-
'anthropic-beta': 'oauth-2025-04-20'
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
try {
|
|
553
|
-
const response = await makeRequest(modelsUrl, { method: 'GET', headers, timeout: 15000 });
|
|
554
|
-
if (response.data && Array.isArray(response.data)) {
|
|
555
|
-
const models = response.data.map(m => m.id).filter(Boolean);
|
|
556
|
-
if (models.length > 0) return { models, error: null };
|
|
557
|
-
}
|
|
558
|
-
return { models: null, error: 'API returned empty or invalid response' };
|
|
559
|
-
} catch (error) {
|
|
560
|
-
return { models: null, error: error.message };
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* Fetch available models from Google Gemini API
|
|
566
|
-
* @param {string} apiKey - API key
|
|
567
|
-
* @returns {Promise<Array|null>} Array of model IDs or null on error
|
|
568
|
-
*
|
|
569
|
-
* Data source: https://generativelanguage.googleapis.com/v1/models (GET)
|
|
570
|
-
*/
|
|
571
|
-
const fetchGeminiModels = async (apiKey) => {
|
|
572
|
-
if (!apiKey) return null;
|
|
573
|
-
|
|
574
|
-
const url = `https://generativelanguage.googleapis.com/v1/models?key=${apiKey}`;
|
|
575
|
-
|
|
576
|
-
try {
|
|
577
|
-
const response = await makeRequest(url, { method: 'GET', timeout: 10000 });
|
|
578
|
-
if (response.models && Array.isArray(response.models)) {
|
|
579
|
-
// Filter only generative models and extract the model name
|
|
580
|
-
return response.models
|
|
581
|
-
.filter(m => m.supportedGenerationMethods?.includes('generateContent'))
|
|
582
|
-
.map(m => m.name.replace('models/', ''))
|
|
583
|
-
.filter(Boolean);
|
|
584
|
-
}
|
|
585
|
-
return null;
|
|
586
|
-
} catch (error) {
|
|
587
|
-
return null;
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
/**
|
|
592
|
-
* Fetch available models from OpenAI-compatible API
|
|
593
|
-
* @param {string} endpoint - API endpoint
|
|
594
|
-
* @param {string} apiKey - API key
|
|
595
|
-
* @returns {Promise<Array|null>} Array of model IDs or null on error
|
|
596
|
-
*
|
|
597
|
-
* Data source: {endpoint}/models (GET)
|
|
598
|
-
*/
|
|
599
|
-
/**
|
|
600
|
-
* Fetch available models from OpenAI-compatible API
|
|
601
|
-
* @param {string} endpoint - API endpoint base URL
|
|
602
|
-
* @param {string} apiKey - API key or OAuth token
|
|
603
|
-
* @returns {Promise<Array|null>} Array of model IDs from API, null if unavailable
|
|
604
|
-
*
|
|
605
|
-
* Data source: {endpoint}/models (GET)
|
|
606
|
-
*/
|
|
607
|
-
const fetchOpenAIModels = async (endpoint, apiKey) => {
|
|
608
|
-
if (!endpoint) return null;
|
|
609
|
-
|
|
610
|
-
const url = `${endpoint}/models`;
|
|
611
|
-
|
|
612
|
-
const headers = {
|
|
613
|
-
'Content-Type': 'application/json'
|
|
614
|
-
};
|
|
615
|
-
|
|
616
|
-
if (apiKey) {
|
|
617
|
-
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
try {
|
|
621
|
-
const response = await makeRequest(url, { method: 'GET', headers, timeout: 10000 });
|
|
622
|
-
if (response.data && Array.isArray(response.data)) {
|
|
623
|
-
// Return models from API - filter to chat models only
|
|
624
|
-
const chatModels = response.data
|
|
625
|
-
.map(m => m.id)
|
|
626
|
-
.filter(id => id && (
|
|
627
|
-
id.includes('gpt') ||
|
|
628
|
-
id.includes('o1') ||
|
|
629
|
-
id.includes('o3') ||
|
|
630
|
-
id.includes('claude') ||
|
|
631
|
-
id.includes('gemini')
|
|
632
|
-
))
|
|
633
|
-
.filter(id =>
|
|
634
|
-
!id.includes('embedding') &&
|
|
635
|
-
!id.includes('whisper') &&
|
|
636
|
-
!id.includes('tts') &&
|
|
637
|
-
!id.includes('dall-e')
|
|
638
|
-
);
|
|
639
|
-
|
|
640
|
-
return chatModels.length > 0 ? chatModels : null;
|
|
641
|
-
}
|
|
642
|
-
return null;
|
|
643
|
-
} catch (error) {
|
|
644
|
-
return null;
|
|
645
|
-
}
|
|
646
|
-
};
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* Fetch available models for OAuth-authenticated providers
|
|
650
|
-
* Uses multiple API endpoints to discover available models
|
|
651
|
-
*
|
|
652
|
-
* @param {string} providerId - Provider ID (anthropic, openai, gemini, etc.)
|
|
653
|
-
* @param {string} accessToken - OAuth access token
|
|
654
|
-
* @returns {Promise<Array|null>} Array of model IDs from API, null if unavailable
|
|
655
|
-
*
|
|
656
|
-
* Data sources:
|
|
657
|
-
* - OpenAI: https://api.openai.com/v1/models (GET)
|
|
658
|
-
* - Anthropic: https://api.anthropic.com/v1/models (GET)
|
|
659
|
-
* - Gemini: https://generativelanguage.googleapis.com/v1/models (GET)
|
|
660
|
-
*/
|
|
661
|
-
const fetchModelsWithOAuth = async (providerId, accessToken) => {
|
|
662
|
-
if (!accessToken) return null;
|
|
663
|
-
|
|
664
|
-
try {
|
|
665
|
-
switch (providerId) {
|
|
666
|
-
case 'anthropic':
|
|
667
|
-
return await fetchAnthropicModelsOAuth(accessToken);
|
|
668
|
-
|
|
669
|
-
case 'openai': {
|
|
670
|
-
// Try OpenAI /v1/models endpoint with OAuth token
|
|
671
|
-
// NO HARDCODED FALLBACK - models must come from API only
|
|
672
|
-
const openaiModels = await fetchOpenAIModels('https://api.openai.com/v1', accessToken);
|
|
673
|
-
if (openaiModels && openaiModels.length > 0) {
|
|
674
|
-
return openaiModels;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// Try alternative: ChatGPT backend API (for Plus/Pro plans)
|
|
678
|
-
try {
|
|
679
|
-
const chatgptUrl = 'https://chatgpt.com/backend-api/models';
|
|
680
|
-
const chatgptHeaders = {
|
|
681
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
682
|
-
'Content-Type': 'application/json'
|
|
683
|
-
};
|
|
684
|
-
const chatgptResponse = await makeRequest(chatgptUrl, {
|
|
685
|
-
method: 'GET',
|
|
686
|
-
headers: chatgptHeaders,
|
|
687
|
-
timeout: 10000
|
|
688
|
-
});
|
|
689
|
-
if (chatgptResponse.models && Array.isArray(chatgptResponse.models)) {
|
|
690
|
-
return chatgptResponse.models
|
|
691
|
-
.map(m => m.slug || m.id || m.name)
|
|
692
|
-
.filter(Boolean);
|
|
693
|
-
}
|
|
694
|
-
} catch (e) {
|
|
695
|
-
if (process.env.HQX_DEBUG) {
|
|
696
|
-
console.error('[DEBUG] ChatGPT backend error:', e.message);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
return null;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
case 'gemini': {
|
|
704
|
-
// Gemini OAuth - fetch from Generative Language API
|
|
705
|
-
// NO HARDCODED FALLBACK - models must come from API only
|
|
706
|
-
try {
|
|
707
|
-
const geminiUrl = 'https://generativelanguage.googleapis.com/v1/models';
|
|
708
|
-
const geminiHeaders = {
|
|
709
|
-
'Authorization': `Bearer ${accessToken}`
|
|
710
|
-
};
|
|
711
|
-
const geminiResponse = await makeRequest(geminiUrl, {
|
|
712
|
-
method: 'GET',
|
|
713
|
-
headers: geminiHeaders,
|
|
714
|
-
timeout: 15000
|
|
715
|
-
});
|
|
716
|
-
if (geminiResponse.models && Array.isArray(geminiResponse.models)) {
|
|
717
|
-
const models = geminiResponse.models
|
|
718
|
-
.filter(m => m.supportedGenerationMethods?.includes('generateContent'))
|
|
719
|
-
.map(m => m.name.replace('models/', ''))
|
|
720
|
-
.filter(Boolean);
|
|
721
|
-
if (models.length > 0) return models;
|
|
722
|
-
}
|
|
723
|
-
} catch (e) {
|
|
724
|
-
if (process.env.HQX_DEBUG) {
|
|
725
|
-
console.error('[DEBUG] Gemini models API error:', e.message);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
return null;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
case 'qwen': {
|
|
733
|
-
// Qwen - try to fetch models from Alibaba Cloud API
|
|
734
|
-
try {
|
|
735
|
-
const qwenUrl = 'https://dashscope.aliyuncs.com/api/v1/models';
|
|
736
|
-
const qwenHeaders = {
|
|
737
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
738
|
-
'Content-Type': 'application/json'
|
|
739
|
-
};
|
|
740
|
-
const qwenResponse = await makeRequest(qwenUrl, {
|
|
741
|
-
method: 'GET',
|
|
742
|
-
headers: qwenHeaders,
|
|
743
|
-
timeout: 10000
|
|
744
|
-
});
|
|
745
|
-
if (qwenResponse.data && Array.isArray(qwenResponse.data)) {
|
|
746
|
-
return qwenResponse.data
|
|
747
|
-
.map(m => m.id || m.model_id)
|
|
748
|
-
.filter(Boolean);
|
|
749
|
-
}
|
|
750
|
-
} catch (e) {
|
|
751
|
-
// Qwen API may not support model listing
|
|
752
|
-
}
|
|
753
|
-
return null;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
case 'iflow': {
|
|
757
|
-
// iFlow - fetch models from iFlow API
|
|
758
|
-
try {
|
|
759
|
-
const iflowUrl = 'https://apis.iflow.cn/v1/models';
|
|
760
|
-
const iflowHeaders = {
|
|
761
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
762
|
-
'Content-Type': 'application/json'
|
|
763
|
-
};
|
|
764
|
-
const iflowResponse = await makeRequest(iflowUrl, {
|
|
765
|
-
method: 'GET',
|
|
766
|
-
headers: iflowHeaders,
|
|
767
|
-
timeout: 10000
|
|
768
|
-
});
|
|
769
|
-
if (iflowResponse.data && Array.isArray(iflowResponse.data)) {
|
|
770
|
-
return iflowResponse.data
|
|
771
|
-
.map(m => m.id)
|
|
772
|
-
.filter(Boolean);
|
|
773
|
-
}
|
|
774
|
-
} catch (e) {
|
|
775
|
-
// iFlow API may not support model listing
|
|
776
|
-
}
|
|
777
|
-
return null;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
default:
|
|
781
|
-
return null;
|
|
782
|
-
}
|
|
783
|
-
} catch (error) {
|
|
784
|
-
return null;
|
|
785
|
-
}
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
/**
|
|
789
|
-
* Fetch models via local CLIProxyAPI proxy
|
|
790
|
-
* @returns {Promise<Array|null>} Array of model IDs or null
|
|
791
|
-
*/
|
|
792
|
-
const fetchModelsViaProxy = async (proxyPort = 8317) => {
|
|
793
|
-
const url = `http://127.0.0.1:${proxyPort}/v1/models`;
|
|
794
|
-
|
|
795
|
-
const headers = {
|
|
796
|
-
'Authorization': 'Bearer hqx-local-key'
|
|
797
|
-
};
|
|
798
|
-
|
|
799
|
-
try {
|
|
800
|
-
const response = await makeRequest(url, { method: 'GET', headers, timeout: 10000 });
|
|
801
|
-
if (response.data && Array.isArray(response.data)) {
|
|
802
|
-
return response.data.map(m => m.id || m).filter(Boolean);
|
|
803
|
-
}
|
|
804
|
-
return null;
|
|
805
|
-
} catch (error) {
|
|
806
|
-
return null;
|
|
807
|
-
}
|
|
808
|
-
};
|
|
322
|
+
// Wrapper functions for analysis (pass callAI to implementation)
|
|
323
|
+
const analyzeTrading = (agent, data) => analyzeTradingImpl(callAI, agent, data);
|
|
324
|
+
const analyzePerformance = (agent, performanceData) => analyzePerformanceImpl(callAI, agent, performanceData);
|
|
325
|
+
const getMarketAdvice = (agent, marketData) => getMarketAdviceImpl(callAI, agent, marketData);
|
|
809
326
|
|
|
810
327
|
module.exports = {
|
|
811
328
|
callAI,
|