fix: 通信成功,从服务器获取合约列表;目前获取详情数据失败

master
Lxy 3 months ago
parent 836fc9bb5f
commit 32ca747deb

@ -99,8 +99,50 @@ def analyze():
# 保存到数据库 # 保存到数据库
data_storage.save_kline_data(symbol, duration, df) 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) data_storage.save_analysis_result(analysis_result)

@ -10,7 +10,6 @@ import riskRoutes from './api/risk';
import configRoutes from './api/config'; import configRoutes from './api/config';
import watchlistRoutes from './api/watchlist'; import watchlistRoutes from './api/watchlist';
import pushRoutes from './api/push'; import pushRoutes from './api/push';
import { pythonServiceManager } from './services/datasource/PythonServiceManager';
const app = express(); 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; const port = config.port || 3007;
// 启动前检查是否需要启动Python TQAPI服务 // 启动服务
async function startServer() { async function startServer() {
try { 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服务器 // 启动Express服务器
const server = app.listen(port, () => { const server = app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`); console.log(`服务器运行在 http://localhost:${port}`);
@ -79,9 +68,6 @@ async function startServer() {
// 处理服务器关闭事件 // 处理服务器关闭事件
function handleShutdown() { function handleShutdown() {
console.log('正在关闭服务器...'); console.log('正在关闭服务器...');
// 关闭Python服务
pythonServiceManager.stop();
console.log('Python服务已关闭');
// 关闭Express服务器 // 关闭Express服务器
server.close(() => { server.close(() => {
console.log('服务器已关闭'); console.log('服务器已关闭');
@ -95,8 +81,6 @@ async function startServer() {
process.on('exit', handleShutdown); process.on('exit', handleShutdown);
} catch (error) { } catch (error) {
console.error('启动服务器时出错:', error); console.error('启动服务器时出错:', error);
// 出错时也要关闭Python服务
pythonServiceManager.stop();
} }
} }

@ -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<T>(endpoint: string, params?: Record<string, string>): Promise<T> {
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<T>(endpoint: string, data?: any): Promise<T> {
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<string, string> = {};
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<string, string> = { 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;

@ -2,6 +2,7 @@
import { DataSourceFactory, DataSourceType } from './datasource/DataSourceFactory'; import { DataSourceFactory, DataSourceType } from './datasource/DataSourceFactory';
import { futuresList, generateFuturesOverview, generateFutureData, generateKlineData, riskAlerts } from '../utils/mockData'; import { futuresList, generateFuturesOverview, generateFutureData, generateKlineData, riskAlerts } from '../utils/mockData';
import { config } from '../config'; import { config } from '../config';
import { serviceImplementationClient } from './ServiceImplementationClient';
// 获取数据源配置 // 获取数据源配置
const getDataSourceConfig = () => { const getDataSourceConfig = () => {
@ -12,6 +13,118 @@ const getDataSourceConfig = () => {
// 获取市场概览 // 获取市场概览
export const fetchMarketOverview = async () => { export const fetchMarketOverview = async () => {
try { 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(); const dataSourceConfig = getDataSourceConfig();
console.log('获取数据源配置:', dataSourceConfig); console.log('获取数据源配置:', dataSourceConfig);
@ -128,6 +241,97 @@ export const fetchMarketDetail = async (symbol: string) => {
throw new Error('品种不存在'); 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(); const dataSourceConfig = getDataSourceConfig();
@ -241,6 +445,67 @@ export const fetchKlineData = async (symbol: string, period: string) => {
throw new Error('品种不存在'); 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(); const dataSourceConfig = getDataSourceConfig();
@ -302,6 +567,84 @@ export const fetchKlineData = async (symbol: string, period: string) => {
// 获取市场热点 // 获取市场热点
export const fetchMarketHotspots = async () => { export const fetchMarketHotspots = async () => {
try { 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(); const dataSourceConfig = getDataSourceConfig();

Loading…
Cancel
Save