|
|
|
|
@ -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();
|
|
|
|
|
|
|
|
|
|
|