diff --git a/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc b/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc index faf89ca..72124ca 100644 Binary files a/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc and b/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc differ diff --git a/backend/service_implementation/service/app.py b/backend/service_implementation/service/app.py index f5e3096..07a3001 100644 --- a/backend/service_implementation/service/app.py +++ b/backend/service_implementation/service/app.py @@ -99,8 +99,50 @@ def analyze(): # 保存到数据库 data_storage.save_kline_data(symbol, duration, df) + # 准备分析所需的数据 + market_data = { + 'symbol': symbol, + 'latest_price': float(df['close'].iloc[-1]) if not df.empty else 0, + 'volume': int(df['volume'].iloc[-1]) if not df.empty else 0, + 'timeframe': duration + } + + # 模拟技术指标数据 + technical_indicators = { + 'macd': {'status': '金叉'}, + 'rsi': 50, + 'bollinger': {'status': '中轨附近'}, + 'kdj': {'status': '金叉'}, + 'atr': 10 + } + + # 模拟趋势分析数据 + trend_analysis = { + 'adx': 30, + 'trend_strength': '中等', + 'trend_direction': '震荡偏多', + 'ma_relationship': '金叉', + 'multi_period_analysis': {'1m': '震荡', '5m': '偏多', '15m': '偏多'}, + 'overall_trend': '震荡偏多', + 'win_rate': 60 + } + + # 模拟风险指标数据 + risk_metrics = { + 'stop_loss': market_data['latest_price'] * 0.98, + 'target_price': market_data['latest_price'] * 1.02, + 'profit_loss_ratio': 2, + 'position_size': 2, + 'risk_ratio': 2 + } + # 执行分析 - analysis_result = deepseek_agent.analyze_market(symbol, df) + analysis_result = deepseek_agent.analyze_market( + market_data, + technical_indicators, + trend_analysis, + risk_metrics + ) # 保存分析结果 data_storage.save_analysis_result(analysis_result) diff --git a/backend/service_implementation/service/data/futures_analysis.db b/backend/service_implementation/service/data/futures_analysis.db index bee98fa..0211f04 100644 Binary files a/backend/service_implementation/service/data/futures_analysis.db and b/backend/service_implementation/service/data/futures_analysis.db differ diff --git a/backend/src/app.ts b/backend/src/app.ts index 4b25ed6..0bc2b36 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -10,7 +10,6 @@ import riskRoutes from './api/risk'; import configRoutes from './api/config'; import watchlistRoutes from './api/watchlist'; import pushRoutes from './api/push'; -import { pythonServiceManager } from './services/datasource/PythonServiceManager'; const app = express(); @@ -58,19 +57,9 @@ app.use((err: any, req: express.Request, res: express.Response, next: express.Ne // 启动服务器 const port = config.port || 3007; -// 启动前检查是否需要启动Python TQAPI服务 +// 启动服务器 async function startServer() { try { - // 检查配置,是否使用TQSDK数据源 - if (config.dataSource?.defaultDataSource === 'tqsdk' || - (config.dataSource?.tqsdk && config.dataSource.tqsdk.enabled)) { - console.log('检测到使用TQSDK数据源,准备启动Python服务...'); - const pythonStarted = await pythonServiceManager.start(); - if (!pythonStarted) { - console.warn('Python服务启动失败,可能会影响TQSDK数据获取'); - } - } - // 启动Express服务器 const server = app.listen(port, () => { console.log(`服务器运行在 http://localhost:${port}`); @@ -79,9 +68,6 @@ async function startServer() { // 处理服务器关闭事件 function handleShutdown() { console.log('正在关闭服务器...'); - // 关闭Python服务 - pythonServiceManager.stop(); - console.log('Python服务已关闭'); // 关闭Express服务器 server.close(() => { console.log('服务器已关闭'); @@ -95,8 +81,6 @@ async function startServer() { process.on('exit', handleShutdown); } catch (error) { console.error('启动服务器时出错:', error); - // 出错时也要关闭Python服务 - pythonServiceManager.stop(); } } diff --git a/backend/src/services/ServiceImplementationClient.ts b/backend/src/services/ServiceImplementationClient.ts new file mode 100644 index 0000000..6786aef --- /dev/null +++ b/backend/src/services/ServiceImplementationClient.ts @@ -0,0 +1,124 @@ +// Service Implementation API Client +// This client interacts with the service_implementation API documented in api_documentation.md + +class ServiceImplementationClient { + private baseUrl: string; + + constructor(baseUrl: string = 'http://localhost:5000') { + this.baseUrl = baseUrl; + } + + async get(endpoint: string, params?: Record): Promise { + let url = `${this.baseUrl}${endpoint}`; + if (params) { + const queryString = new URLSearchParams(params).toString(); + url += `?${queryString}`; + } + + try { + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); + } + + const data = await response.json(); + return data as T; + } catch (error: any) { + console.error('GET request failed:', error.message || error); + throw new Error(`Network request failed: ${error.message || error}`); + } + } + + async post(endpoint: string, data?: any): Promise { + const url = `${this.baseUrl}${endpoint}`; + + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: data ? JSON.stringify(data) : undefined + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); + } + + const responseData = await response.json(); + return responseData as T; + } catch (error: any) { + console.error('POST request failed:', error.message || error); + throw new Error(`Network request failed: ${error.message || error}`); + } + } + + // Health check + async healthCheck() { + return this.get<{ status: string; message: string }>('/health'); + } + + // Get contracts + async getContracts(exchange?: string, symbol?: string) { + const params: Record = {}; + if (exchange) params.exchange = exchange; + if (symbol) params.symbol = symbol; + return this.get<{ status: string; data: any[] }>('/api/contracts', params); + } + + // Get Kline data + async getKlineData(symbol: string, duration: string = '1m', limit: number = 100) { + return this.get<{ status: string; data: any[] }>('/api/kline', { + symbol, + duration, + limit: limit.toString() + }); + } + + // Analyze market + async analyzeMarket(symbol: string, duration: string = '1m', analysisType: string = 'technical') { + return this.post<{ status: string; data: any }>('/api/analyze', { + symbol, + duration, + analysis_type: analysisType + }); + } + + // Get recommendations + async getRecommendations(symbol: string, status?: string) { + const params: Record = { symbol }; + if (status) params.status = status; + return this.get<{ status: string; data: any[] }>('/api/recommendations', params); + } + + // Risk monitoring + async monitorRisk(symbol: string, currentPrice: number, entryPrice: number, stopLoss: number, targetPrice: number) { + return this.post<{ status: string; data: any }>('/api/risk', { + symbol, + current_price: currentPrice, + entry_price: entryPrice, + stop_loss: stopLoss, + target_price: targetPrice + }); + } + + // Get analysis history + async getAnalysisHistory(symbol: string, limit: number = 100) { + return this.get<{ status: string; data: any[] }>('/api/analysis/history', { + symbol, + limit: limit.toString() + }); + } +} + +// Export singleton instance +export const serviceImplementationClient = new ServiceImplementationClient(); +export default ServiceImplementationClient; diff --git a/backend/src/services/marketService.ts b/backend/src/services/marketService.ts index ada1db3..5d24b3e 100644 --- a/backend/src/services/marketService.ts +++ b/backend/src/services/marketService.ts @@ -2,6 +2,7 @@ import { DataSourceFactory, DataSourceType } from './datasource/DataSourceFactory'; import { futuresList, generateFuturesOverview, generateFutureData, generateKlineData, riskAlerts } from '../utils/mockData'; import { config } from '../config'; +import { serviceImplementationClient } from './ServiceImplementationClient'; // 获取数据源配置 const getDataSourceConfig = () => { @@ -12,6 +13,118 @@ const getDataSourceConfig = () => { // 获取市场概览 export const fetchMarketOverview = async () => { try { + // 首先尝试使用 service_implementation API + try { + console.log('尝试使用 service_implementation API 获取市场概览...'); + + // 先获取合约列表 + console.log('获取合约列表...'); + const contractsResponse = await serviceImplementationClient.getContracts(); + const allContracts = contractsResponse.data; + + // 去重,按品种代码分组 + const uniqueContracts = new Map(); + for (const contract of allContracts) { + // 提取品种代码(如从CU2603中提取CU) + const code = contract.symbol.slice(0, 2); + if (!uniqueContracts.has(code)) { + uniqueContracts.set(code, { + code: code, + name: contract.name || code, + exchange: contract.exchange || '' + }); + } + } + + // 转换为数组 + const contractList = Array.from(uniqueContracts.values()); + console.log(`获取到 ${contractList.length} 个独特品种`); + + // 使用获取到的合约列表 + const overview = []; + for (const future of contractList) { + try { + // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) + const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; + + // 获取合约详情 + console.log(`获取合约${symbol}详情...`); + const contractsResponse = await serviceImplementationClient.getContracts(future.exchange, future.code); + const contract = contractsResponse.data.find((c: any) => c.symbol === symbol); + + if (!contract) { + console.warn(`合约${symbol}不存在,跳过`); + continue; + } + + // 获取分析数据 + console.log(`分析合约${symbol}...`); + const analysisResponse = await serviceImplementationClient.analyzeMarket(symbol); + const analysis = analysisResponse.data; + + overview.push({ + code: future.code, + name: future.name, + currentPrice: analysis.current_price || 0, + changePercent: analysis.change_percent || 0, + winRate: analysis.probability ? Math.round(analysis.probability * 100) : Math.floor(Math.random() * 50) + 30, // 使用分析结果或模拟胜率 + atr: analysis.atr || +(Math.random() * 5 + 0.5).toFixed(2), // 使用分析结果或模拟ATR + adx: analysis.adx || Math.floor(Math.random() * 60) + 10, // 使用分析结果或模拟ADX + adxStatus: (adx: number) => { + if (adx < 20) return '无趋势/震荡'; + if (adx < 40) return '弱趋势'; + return '强趋势'; + }, + trends: { + '5MIN': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + }, + '30MIN': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + }, + '1HOUR': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + }, + '1DAY': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + } + }, + tradingAdvice: { + entry: analysis.entry_price || 0, + stopLoss: analysis.stop_loss || 0, + target: analysis.target_price || 0, + resistance: analysis.resistance || 0, + support: analysis.support || 0 + }, + overallView: analysis.trend === 'up' ? '多头排列' : analysis.trend === 'down' ? '空头排列' : '震荡', + aiAnalysis: `趋势:${analysis.trend || '中性'} | 概率:${analysis.probability ? Math.round(analysis.probability * 100) : 50}% | 方向:${analysis.direction || '观望'}` + }); + } catch (error) { + console.error(`获取合约${future.code}行情失败:`, error); + // 跳过获取失败的合约 + continue; + } + } + + if (overview.length > 0) { + console.log('使用 service_implementation API 获取市场概览成功'); + return overview; + } + + console.warn('service_implementation API 未返回数据,尝试使用其他数据源'); + } catch (error) { + console.error('service_implementation API 获取失败:', error); + // service_implementation API 失败,尝试使用其他数据源 + } + // 获取数据源配置 const dataSourceConfig = getDataSourceConfig(); console.log('获取数据源配置:', dataSourceConfig); @@ -128,6 +241,97 @@ export const fetchMarketDetail = async (symbol: string) => { throw new Error('品种不存在'); } + // 首先尝试使用 service_implementation API + try { + console.log('尝试使用 service_implementation API 获取品种详情...'); + + // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) + const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; + + // 获取合约详情 + console.log(`获取合约${contractSymbol}详情...`); + const contractsResponse = await serviceImplementationClient.getContracts(future.exchange, future.code); + const contract = contractsResponse.data.find((c: any) => c.symbol === contractSymbol); + + if (!contract) { + console.warn(`合约${contractSymbol}不存在,尝试使用其他数据源`); + } else { + // 获取分析数据 + console.log(`分析合约${contractSymbol}...`); + const analysisResponse = await serviceImplementationClient.analyzeMarket(contractSymbol); + const analysis = analysisResponse.data; + + // 获取交易建议 + console.log(`获取合约${contractSymbol}交易建议...`); + const recommendationsResponse = await serviceImplementationClient.getRecommendations(contractSymbol); + const recommendation = recommendationsResponse.data[0]; + + // 转换为前端需要的格式 + const result = { + code: future.code, + name: future.name, + fullName: `${future.name}-${future.code}605`, + currentPrice: analysis.current_price || 0, + changePercent: analysis.change_percent || 0, + winRate: analysis.probability ? Math.round(analysis.probability * 100) : Math.floor(Math.random() * 50) + 30, + atr: analysis.atr || +(Math.random() * 5 + 0.5).toFixed(2), + adx: analysis.adx || Math.floor(Math.random() * 60) + 10, + adxStatus: (adx: number) => { + if (adx < 20) return '无趋势/震荡'; + if (adx < 40) return '弱趋势'; + return '强趋势'; + }, + trends: { + '5MIN': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + }, + '30MIN': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + }, + '1HOUR': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + }, + '1DAY': { + direction: analysis.trend === 'up' ? '看多' : analysis.trend === 'down' ? '看空' : '观望', + status: analysis.trend === 'up' ? '多头趋势' : analysis.trend === 'down' ? '空头趋势' : '震荡', + rsi: Math.floor(Math.random() * 80) + 10 + } + }, + indicators: { + macd: analysis.trend === 'up' ? '金叉向上' : analysis.trend === 'down' ? '死叉向下' : '走平', + rsi: `${Math.floor(Math.random() * 80) + 10}(中性)`, + bollinger: ['触及上轨', '触及下轨', '中轨附近'][Math.floor(Math.random() * 3)], + kdj: analysis.trend === 'up' ? '金叉向上' : analysis.trend === 'down' ? '死叉向下' : '走平' + }, + tradingAdvice: { + entry: analysis.entry_price || recommendation?.entry_price || 0, + stopLoss: analysis.stop_loss || recommendation?.stop_loss || 0, + target: analysis.target_price || recommendation?.target_price || 0, + resistance: analysis.resistance || 0, + support: analysis.support || 0 + }, + riskLevel: analysis.risk_level || ['低', '中等', '高'][Math.floor(Math.random() * 3)], + volatility: ['低', '中等', '高'][Math.floor(Math.random() * 3)], + overallView: analysis.trend === 'up' ? '多头排列' : analysis.trend === 'down' ? '空头排列' : '震荡', + aiAnalysis: `趋势:${analysis.trend || '中性'} | 概率:${analysis.probability ? Math.round(analysis.probability * 100) : 50}% | 方向:${analysis.direction || '观望'}` + }; + + console.log('使用 service_implementation API 获取品种详情成功'); + return result; + } + + console.warn('service_implementation API 未返回数据,尝试使用其他数据源'); + } catch (error) { + console.error('service_implementation API 获取失败:', error); + // service_implementation API 失败,尝试使用其他数据源 + } + // 获取数据源配置 const dataSourceConfig = getDataSourceConfig(); @@ -241,6 +445,67 @@ export const fetchKlineData = async (symbol: string, period: string) => { throw new Error('品种不存在'); } + // 首先尝试使用 service_implementation API + try { + console.log('尝试使用 service_implementation API 获取K线数据...'); + + // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) + const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; + + // 转换周期格式 + let duration = period; + switch (period) { + case '1MIN': + duration = '1m'; + break; + case '5MIN': + duration = '5m'; + break; + case '15MIN': + duration = '15m'; + break; + case '30MIN': + duration = '30m'; + break; + case '1HOUR': + duration = '1h'; + break; + case '4HOUR': + duration = '4h'; + break; + case '1DAY': + duration = '1d'; + break; + default: + duration = '1m'; + } + + // 获取K线数据 + console.log(`获取合约${contractSymbol}K线数据,周期: ${duration}...`); + const klineResponse = await serviceImplementationClient.getKlineData(contractSymbol, duration, 30); + const klineData = klineResponse.data; + + if (klineData.length > 0) { + // 转换为前端需要的格式 + const result = klineData.map((item: any) => ({ + timestamp: new Date(item.datetime).getTime() / 1000, // 转换为秒 + open: item.open, + high: item.high, + low: item.low, + close: item.close, + volume: item.volume + })); + + console.log('使用 service_implementation API 获取K线数据成功'); + return result; + } + + console.warn('service_implementation API 未返回K线数据,尝试使用其他数据源'); + } catch (error) { + console.error('service_implementation API 获取K线数据失败:', error); + // service_implementation API 失败,尝试使用其他数据源 + } + // 获取数据源配置 const dataSourceConfig = getDataSourceConfig(); @@ -302,6 +567,84 @@ export const fetchKlineData = async (symbol: string, period: string) => { // 获取市场热点 export const fetchMarketHotspots = async () => { try { + // 首先尝试使用 service_implementation API + try { + console.log('尝试使用 service_implementation API 获取市场热点...'); + + // 先获取合约列表 + console.log('获取合约列表...'); + const contractsResponse = await serviceImplementationClient.getContracts(); + const allContracts = contractsResponse.data; + + // 去重,按品种代码分组 + const uniqueContracts = new Map(); + for (const contract of allContracts) { + // 提取品种代码(如从CU2603中提取CU) + const code = contract.symbol.slice(0, 2); + if (!uniqueContracts.has(code)) { + uniqueContracts.set(code, { + code: code, + name: contract.name || code, + exchange: contract.exchange || '' + }); + } + } + + // 转换为数组 + const contractList = Array.from(uniqueContracts.values()); + console.log(`获取到 ${contractList.length} 个独特品种`); + + // 使用获取到的合约列表 + const hotspots = []; + for (const future of contractList) { + try { + // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) + const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; + + // 获取合约详情 + console.log(`获取合约${symbol}详情...`); + const contractsResponse = await serviceImplementationClient.getContracts(future.exchange, future.code); + const contract = contractsResponse.data.find((c: any) => c.symbol === symbol); + + if (!contract) { + console.warn(`合约${symbol}不存在,跳过`); + continue; + } + + // 获取分析数据 + console.log(`分析合约${symbol}...`); + const analysisResponse = await serviceImplementationClient.analyzeMarket(symbol); + const analysis = analysisResponse.data; + + hotspots.push({ + symbol: future.code, + name: future.name, + change: analysis.change_percent || 0, + volume: analysis.volume || Math.floor(Math.random() * 1000000) + 100000 + }); + } catch (error) { + console.error(`获取合约${future.code}行情失败:`, error); + // 跳过获取失败的合约 + continue; + } + } + + if (hotspots.length > 0) { + // 按涨跌幅排序,返回前10个 + const sortedHotspots = hotspots + .sort((a, b) => Math.abs(b.change) - Math.abs(a.change)) + .slice(0, 10); + + console.log('使用 service_implementation API 获取市场热点成功'); + return sortedHotspots; + } + + console.warn('service_implementation API 未返回市场热点数据,尝试使用其他数据源'); + } catch (error) { + console.error('service_implementation API 获取市场热点失败:', error); + // service_implementation API 失败,尝试使用其他数据源 + } + // 获取数据源配置 const dataSourceConfig = getDataSourceConfig();