fix: 可正常获取K线数据;目前存在问题:1、前端显示K线存在问题;2、请求K线慢 ;解决方案:1、查找K线图显示问题;2、使用缓存策略,提前缓存各个品种的k线数据

master
Lxy 3 months ago
parent 0be07b8f8a
commit 6d0b554cee

@ -214,7 +214,7 @@ class RqDataAdapter(BaseDataAdapter):
print(f"无法获取真实数据:{'API未连接' if not self.api_connected else 'RQData不可用'}") print(f"无法获取真实数据:{'API未连接' if not self.api_connected else 'RQData不可用'}")
return None return None
except Exception as e: except Exception as e:
print(f"获取K线数据失败{e}") print(f"rqdata获取K线数据失败{e}")
# 不再自动返回模拟数据返回None # 不再自动返回模拟数据返回None
return None return None

@ -192,7 +192,7 @@ class TqSdkAdapter(BaseDataAdapter):
print(f"无法获取真实数据:{'API未连接' if not self.api else 'TQSDK不可用'}") print(f"无法获取真实数据:{'API未连接' if not self.api else 'TQSDK不可用'}")
return None return None
except Exception as e: except Exception as e:
print(f"获取K线数据失败{e}") print(f"tqsdk获取K线数据失败{e}")
# 不再自动返回模拟数据返回None # 不再自动返回模拟数据返回None
return None return None

@ -39,7 +39,9 @@ def get_contracts():
exchange = request.args.get('exchange', '') exchange = request.args.get('exchange', '')
symbol = request.args.get('symbol', '') symbol = request.args.get('symbol', '')
print("正在获取合约数据..., exchange:", exchange, "symbol:", symbol)
contracts = data_fetcher.get_contracts(exchange=exchange, symbol=symbol) contracts = data_fetcher.get_contracts(exchange=exchange, symbol=symbol)
print(f"获取到 {contracts} 合约")
return jsonify({'status': 'success', 'data': contracts}) return jsonify({'status': 'success', 'data': contracts})
except Exception as e: except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500 return jsonify({'status': 'error', 'message': str(e)}), 500

@ -73,6 +73,7 @@ class ServiceImplementationClient {
const params: Record<string, string> = {}; const params: Record<string, string> = {};
if (exchange) params.exchange = exchange; if (exchange) params.exchange = exchange;
if (symbol) params.symbol = symbol; if (symbol) params.symbol = symbol;
logger.info(`Getting contracts for exchange ${exchange} and symbol ${symbol}`);
return this.get<{ status: string; data: any[] }>('/api/contracts', params); return this.get<{ status: string; data: any[] }>('/api/contracts', params);
} }

@ -39,14 +39,14 @@ export const fetchMarketOverview = async () => {
// 转换为数组 // 转换为数组
const contractList = Array.from(uniqueContracts.values()); const contractList = Array.from(uniqueContracts.values());
logger.log(`获取到 ${contractList.length} 个独特品种`); logger.log(`获取到 ${contractList.length} 个独特品种,分别是: ${contractList.map(c => c.code).join(', ')}`);
// 使用获取到的合约列表 // 使用获取到的合约列表
const overview = []; const overview = [];
for (const future of contractList) { for (const future of contractList) {
try { try {
// 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写)
const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}03`;
// 获取合约详情 // 获取合约详情
logger.log(`获取合约${symbol}详情...`); logger.log(`获取合约${symbol}详情...`);
@ -131,104 +131,104 @@ export const fetchMarketOverview = async () => {
// service_implementation API 失败,尝试使用其他数据源 // service_implementation API 失败,尝试使用其他数据源
} }
// 获取数据源配置 // // 获取数据源配置
const dataSourceConfig = getDataSourceConfig(); // const dataSourceConfig = getDataSourceConfig();
logger.log('获取数据源配置:', dataSourceConfig); // logger.log('获取数据源配置:', dataSourceConfig);
// 检查是否有可用的数据源 // // 检查是否有可用的数据源
const hasAvailableDataSource = dataSourceConfig.tqsdk?.enabled || dataSourceConfig.test?.enabled; // const hasAvailableDataSource = dataSourceConfig.tqsdk?.enabled || dataSourceConfig.test?.enabled;
if (!hasAvailableDataSource) { // if (!hasAvailableDataSource) {
throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); // throw new Error('无可用数据源,请在管理配置中启用至少一个数据源');
} // }
// 尝试使用TQSDK数据源 // // 尝试使用TQSDK数据源
if (dataSourceConfig.tqsdk?.enabled) { // if (dataSourceConfig.tqsdk?.enabled) {
try { // try {
const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK, dataSourceConfig); // const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK, dataSourceConfig);
// 使用用户指定的合约列表 // // 使用用户指定的合约列表
const overview = []; // const overview = [];
for (const future of futuresList) { // for (const future of futuresList) {
try { // try {
// 构建合约符号使用小写代码因为TQAPI期望小写 // // 构建合约符号使用小写代码因为TQAPI期望小写
const symbol = `${future.exchange}.${future.code.toLowerCase()}${new Date().getFullYear().toString().slice(-2)}05`; // const symbol = `${future.exchange}.${future.code.toLowerCase()}${new Date().getFullYear().toString().slice(-2)}05`;
// 获取合约详情和实时行情 // // 获取合约详情和实时行情
const tick = await dataSource.getTickData(symbol); // const tick = await dataSource.getTickData(symbol);
overview.push({ // overview.push({
code: future.code, // code: future.code,
name: future.name, // name: future.name,
currentPrice: tick.last_price, // currentPrice: tick.last_price,
changePercent: tick.price_change / tick.pre_close * 100, // changePercent: tick.price_change / tick.pre_close * 100,
winRate: Math.floor(Math.random() * 50) + 30, // 模拟胜率 // winRate: Math.floor(Math.random() * 50) + 30, // 模拟胜率
atr: +(Math.random() * 5 + 0.5).toFixed(2), // 模拟ATR // atr: +(Math.random() * 5 + 0.5).toFixed(2), // 模拟ATR
adx: Math.floor(Math.random() * 60) + 10, // 模拟ADX // adx: Math.floor(Math.random() * 60) + 10, // 模拟ADX
adxStatus: (adx: number) => { // adxStatus: (adx: number) => {
if (adx < 20) return '无趋势/震荡'; // if (adx < 20) return '无趋势/震荡';
if (adx < 40) return '弱趋势'; // if (adx < 40) return '弱趋势';
return '强趋势'; // return '强趋势';
}, // },
trends: { // trends: {
'5MIN': { // '5MIN': {
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)], // direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)], // status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
rsi: Math.floor(Math.random() * 80) + 10 // rsi: Math.floor(Math.random() * 80) + 10
}, // },
'30MIN': { // '30MIN': {
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)], // direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)], // status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
rsi: Math.floor(Math.random() * 80) + 10 // rsi: Math.floor(Math.random() * 80) + 10
}, // },
'1HOUR': { // '1HOUR': {
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)], // direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)], // status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
rsi: Math.floor(Math.random() * 80) + 10 // rsi: Math.floor(Math.random() * 80) + 10
}, // },
'1DAY': { // '1DAY': {
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)], // direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)], // status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
rsi: Math.floor(Math.random() * 80) + 10 // rsi: Math.floor(Math.random() * 80) + 10
} // }
}, // },
tradingAdvice: { // tradingAdvice: {
entry: tick.last_price, // entry: tick.last_price,
stopLoss: tick.last_price * (1 - 0.02 * (Math.random() + 0.5)), // stopLoss: tick.last_price * (1 - 0.02 * (Math.random() + 0.5)),
target: tick.last_price * (1 + 0.03 * (Math.random() + 0.5)), // target: tick.last_price * (1 + 0.03 * (Math.random() + 0.5)),
resistance: tick.last_price * (1 + 0.05 * (Math.random() + 0.5)), // resistance: tick.last_price * (1 + 0.05 * (Math.random() + 0.5)),
support: tick.last_price * (1 - 0.05 * (Math.random() + 0.5)) // support: tick.last_price * (1 - 0.05 * (Math.random() + 0.5))
}, // },
overallView: ['观望', '中线', '多头排列', '空头排列', '震荡'][Math.floor(Math.random() * 5)], // overallView: ['观望', '中线', '多头排列', '空头排列', '震荡'][Math.floor(Math.random() * 5)],
aiAnalysis: `MACD:金叉向上 | RSI:${Math.floor(Math.random() * 80) + 10}(中性) | 布林带:中轨附近` // aiAnalysis: `MACD:金叉向上 | RSI:${Math.floor(Math.random() * 80) + 10}(中性) | 布林带:中轨附近`
}); // });
} catch (error) { // } catch (error) {
logger.error(`获取合约${future.code}行情失败:`, error); // logger.error(`获取合约${future.code}行情失败:`, error);
// 跳过获取失败的合约 // // 跳过获取失败的合约
continue; // continue;
} // }
} // }
return overview; // return overview;
} catch (error) { // } catch (error) {
logger.error('TQSDK数据源获取失败:', error); // logger.error('TQSDK数据源获取失败:', error);
// TQSDK数据源失败尝试使用测试数据源 // // TQSDK数据源失败尝试使用测试数据源
if (dataSourceConfig.test?.enabled) { // if (dataSourceConfig.test?.enabled) {
logger.log('切换到测试数据源'); // logger.log('切换到测试数据源');
// 启用了测试数据源,使用测试数据 // // 启用了测试数据源,使用测试数据
await new Promise(resolve => setTimeout(resolve, 300)); // await new Promise(resolve => setTimeout(resolve, 300));
return generateFuturesOverview(); // return generateFuturesOverview();
} else { // } else {
// 未启用测试数据源,返回友好的错误提示 // // 未启用测试数据源,返回友好的错误提示
throw new Error('获取市场概览失败,所有数据源均不可用'); // throw new Error('获取市场概览失败,所有数据源均不可用');
} // }
} // }
} else if (dataSourceConfig.test?.enabled) { // } else if (dataSourceConfig.test?.enabled) {
// 直接使用测试数据源 // // 直接使用测试数据源
logger.log('使用测试数据源'); // logger.log('使用测试数据源');
await new Promise(resolve => setTimeout(resolve, 300)); // await new Promise(resolve => setTimeout(resolve, 300));
return generateFuturesOverview(); // return generateFuturesOverview();
} else { // } else {
// 无可用数据源 // // 无可用数据源
throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); // throw new Error('无可用数据源,请在管理配置中启用至少一个数据源');
} // }
} catch (error) { } catch (error) {
logger.error('获取市场概览失败:', error); logger.error('获取市场概览失败:', error);
// 直接返回友好的错误提示 // 直接返回友好的错误提示
@ -252,7 +252,7 @@ export const fetchMarketDetail = async (symbol: string) => {
logger.log('尝试使用 service_implementation API 获取品种详情...'); logger.log('尝试使用 service_implementation API 获取品种详情...');
// 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写)
const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}03`;
// 获取合约详情 // 获取合约详情
logger.log(`获取合约${contractSymbol}详情...`); logger.log(`获取合约${contractSymbol}详情...`);
@ -461,7 +461,7 @@ export const fetchKlineData = async (symbol: string, period: string) => {
logger.log('尝试使用 service_implementation API 获取K线数据...'); logger.log('尝试使用 service_implementation API 获取K线数据...');
// 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写)
const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}3`;
// 转换周期格式 // 转换周期格式
let duration = period; let duration = period;
@ -517,57 +517,57 @@ export const fetchKlineData = async (symbol: string, period: string) => {
// service_implementation API 失败,尝试使用其他数据源 // service_implementation API 失败,尝试使用其他数据源
} }
// 获取数据源配置 // // 获取数据源配置
const dataSourceConfig = getDataSourceConfig(); // const dataSourceConfig = getDataSourceConfig();
// 检查是否有可用的数据源 // // 检查是否有可用的数据源
const hasAvailableDataSource = dataSourceConfig.tqsdk?.enabled || dataSourceConfig.test?.enabled; // const hasAvailableDataSource = dataSourceConfig.tqsdk?.enabled || dataSourceConfig.test?.enabled;
if (!hasAvailableDataSource) { // if (!hasAvailableDataSource) {
throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); // throw new Error('无可用数据源,请在管理配置中启用至少一个数据源');
} // }
// 尝试使用TQSDK数据源 // // 尝试使用TQSDK数据源
if (dataSourceConfig.tqsdk?.enabled) { // if (dataSourceConfig.tqsdk?.enabled) {
try { // try {
const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK, dataSourceConfig); // const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK, dataSourceConfig);
// 构建合约符号使用小写代码因为TQAPI期望小写 // // 构建合约符号使用小写代码因为TQAPI期望小写
const contractSymbol = `${future.exchange}.${future.code.toLowerCase()}${new Date().getFullYear().toString().slice(-2)}05`; // const contractSymbol = `${future.exchange}.${future.code.toLowerCase()}${new Date().getFullYear().toString().slice(-2)}05`;
// 获取K线数据 // // 获取K线数据
const klineData = await dataSource.getKlineData(contractSymbol, period, 30); // const klineData = await dataSource.getKlineData(contractSymbol, period, 30);
// 转换为前端需要的格式 // // 转换为前端需要的格式
return klineData.map(item => ({ // return klineData.map(item => ({
timestamp: item.datetime / 1000000000, // 转换为秒 // timestamp: item.datetime / 1000000000, // 转换为秒
open: item.open, // open: item.open,
high: item.high, // high: item.high,
low: item.low, // low: item.low,
close: item.close, // close: item.close,
volume: item.volume // volume: item.volume
})); // }));
} catch (error) { // } catch (error) {
logger.error('TQSDK数据源获取失败:', error); // logger.error('TQSDK数据源获取失败:', error);
// TQSDK数据源失败尝试使用测试数据源 // // TQSDK数据源失败尝试使用测试数据源
if (dataSourceConfig.test?.enabled) { // if (dataSourceConfig.test?.enabled) {
logger.log('切换到测试数据源'); // logger.log('切换到测试数据源');
// 启用了测试数据源,使用测试数据 // // 启用了测试数据源,使用测试数据
await new Promise(resolve => setTimeout(resolve, 200)); // await new Promise(resolve => setTimeout(resolve, 200));
return generateKlineData(30); // return generateKlineData(30);
} else { // } else {
// 未启用测试数据源,返回友好的错误提示 // // 未启用测试数据源,返回友好的错误提示
throw new Error('获取K线数据失败所有数据源均不可用'); // throw new Error('获取K线数据失败所有数据源均不可用');
} // }
} // }
} else if (dataSourceConfig.test?.enabled) { // } else if (dataSourceConfig.test?.enabled) {
// 直接使用测试数据源 // // 直接使用测试数据源
logger.log('使用测试数据源'); // logger.log('使用测试数据源');
await new Promise(resolve => setTimeout(resolve, 200)); // await new Promise(resolve => setTimeout(resolve, 200));
return generateKlineData(30); // return generateKlineData(30);
} else { // } else {
// 无可用数据源 // // 无可用数据源
throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); // throw new Error('无可用数据源,请在管理配置中启用至少一个数据源');
} // }
} catch (error) { } catch (error) {
logger.error(`获取合约${symbol}K线数据失败:`, error); logger.error(`获取合约${symbol}K线数据失败:`, error);
// 直接返回友好的错误提示 // 直接返回友好的错误提示
@ -610,7 +610,7 @@ export const fetchMarketHotspots = async () => {
for (const future of contractList) { for (const future of contractList) {
try { try {
// 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写)
const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}03`;
// 获取合约详情 // 获取合约详情
logger.log(`获取合约${symbol}详情...`); logger.log(`获取合约${symbol}详情...`);

Loading…
Cancel
Save