|
|
|
|
@ -1,51 +1,230 @@
|
|
|
|
|
// 市场数据服务
|
|
|
|
|
import { DataSourceFactory, DataSourceType } from './datasource/DataSourceFactory';
|
|
|
|
|
import { futuresList, generateFuturesOverview, generateFutureData, generateKlineData, riskAlerts } from '../utils/mockData';
|
|
|
|
|
import { config } from '../config';
|
|
|
|
|
|
|
|
|
|
// 获取数据源配置
|
|
|
|
|
const getDataSourceConfig = () => {
|
|
|
|
|
// 这里应该从配置文件或数据库中读取数据源配置
|
|
|
|
|
// 暂时返回默认配置
|
|
|
|
|
return {
|
|
|
|
|
tqsdk: {
|
|
|
|
|
enabled: true,
|
|
|
|
|
apiKey: '',
|
|
|
|
|
apiSecret: '',
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
|
|
|
|
timeout: 30000,
|
|
|
|
|
retries: 3,
|
|
|
|
|
maxConnections: 5
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取市场概览
|
|
|
|
|
export const fetchMarketOverview = async () => {
|
|
|
|
|
// 模拟API请求延迟
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
return generateFuturesOverview();
|
|
|
|
|
try {
|
|
|
|
|
// 获取数据源配置
|
|
|
|
|
const dataSourceConfig = getDataSourceConfig();
|
|
|
|
|
// 使用TQSDK数据源
|
|
|
|
|
const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK, dataSourceConfig);
|
|
|
|
|
const overview = await dataSource.getMarketOverview();
|
|
|
|
|
|
|
|
|
|
// 转换为前端需要的格式
|
|
|
|
|
return overview.map(item => ({
|
|
|
|
|
code: item.symbol,
|
|
|
|
|
name: item.name,
|
|
|
|
|
currentPrice: item.price,
|
|
|
|
|
changePercent: item.change_percent,
|
|
|
|
|
winRate: Math.floor(Math.random() * 50) + 30, // 模拟胜率
|
|
|
|
|
atr: +(Math.random() * 5 + 0.5).toFixed(2), // 模拟ATR
|
|
|
|
|
adx: Math.floor(Math.random() * 60) + 10, // 模拟ADX
|
|
|
|
|
adxStatus: adx => {
|
|
|
|
|
if (adx < 20) return '无趋势/震荡';
|
|
|
|
|
if (adx < 40) return '弱趋势';
|
|
|
|
|
return '强趋势';
|
|
|
|
|
},
|
|
|
|
|
trends: {
|
|
|
|
|
'5MIN': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
},
|
|
|
|
|
'30MIN': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
},
|
|
|
|
|
'1HOUR': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
},
|
|
|
|
|
'1DAY': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
tradingAdvice: {
|
|
|
|
|
entry: item.price,
|
|
|
|
|
stopLoss: item.price * (1 - 0.02 * (Math.random() + 0.5)),
|
|
|
|
|
target: item.price * (1 + 0.03 * (Math.random() + 0.5)),
|
|
|
|
|
resistance: item.price * (1 + 0.05 * (Math.random() + 0.5)),
|
|
|
|
|
support: item.price * (1 - 0.05 * (Math.random() + 0.5))
|
|
|
|
|
},
|
|
|
|
|
overallView: ['观望', '中线', '多头排列', '空头排列', '震荡'][Math.floor(Math.random() * 5)],
|
|
|
|
|
aiAnalysis: `MACD:金叉向上 | RSI:${Math.floor(Math.random() * 80) + 10}(中性) | 布林带:中轨附近`
|
|
|
|
|
}));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取市场概览失败:', error);
|
|
|
|
|
// 失败时使用模拟数据
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
return generateFuturesOverview();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取品种详情
|
|
|
|
|
export const fetchMarketDetail = async (symbol: string) => {
|
|
|
|
|
// 模拟API请求延迟
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
|
const future = futuresList.find(item => item.code === symbol);
|
|
|
|
|
if (!future) {
|
|
|
|
|
throw new Error('品种不存在');
|
|
|
|
|
try {
|
|
|
|
|
// 获取数据源配置
|
|
|
|
|
const dataSourceConfig = getDataSourceConfig();
|
|
|
|
|
// 使用TQSDK数据源
|
|
|
|
|
const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK, dataSourceConfig);
|
|
|
|
|
const contract = await dataSource.getContractDetail(symbol);
|
|
|
|
|
const tick = await dataSource.getTickData(symbol);
|
|
|
|
|
|
|
|
|
|
// 转换为前端需要的格式
|
|
|
|
|
return {
|
|
|
|
|
code: contract.symbol,
|
|
|
|
|
name: contract.name,
|
|
|
|
|
fullName: `${contract.name}-${contract.symbol}605`,
|
|
|
|
|
currentPrice: tick.last_price,
|
|
|
|
|
changePercent: tick.price_change / tick.pre_close * 100,
|
|
|
|
|
winRate: Math.floor(Math.random() * 50) + 30,
|
|
|
|
|
atr: +(Math.random() * 5 + 0.5).toFixed(2),
|
|
|
|
|
adx: Math.floor(Math.random() * 60) + 10,
|
|
|
|
|
adxStatus: adx => {
|
|
|
|
|
if (adx < 20) return '无趋势/震荡';
|
|
|
|
|
if (adx < 40) return '弱趋势';
|
|
|
|
|
return '强趋势';
|
|
|
|
|
},
|
|
|
|
|
trends: {
|
|
|
|
|
'5MIN': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
},
|
|
|
|
|
'30MIN': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
},
|
|
|
|
|
'1HOUR': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
},
|
|
|
|
|
'1DAY': {
|
|
|
|
|
direction: ['看多', '看空', '观望'][Math.floor(Math.random() * 3)],
|
|
|
|
|
status: ['多头趋势', '空头趋势', '震荡'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: Math.floor(Math.random() * 80) + 10
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
indicators: {
|
|
|
|
|
macd: ['金叉向上', '死叉向下', '走平'][Math.floor(Math.random() * 3)],
|
|
|
|
|
rsi: `${Math.floor(Math.random() * 80) + 10}(中性)`,
|
|
|
|
|
bollinger: ['触及上轨', '触及下轨', '中轨附近'][Math.floor(Math.random() * 3)],
|
|
|
|
|
kdj: ['金叉向上', '死叉向下', '走平'][Math.floor(Math.random() * 3)]
|
|
|
|
|
},
|
|
|
|
|
tradingAdvice: {
|
|
|
|
|
entry: tick.last_price,
|
|
|
|
|
stopLoss: tick.last_price * (1 - 0.02 * (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)),
|
|
|
|
|
support: tick.last_price * (1 - 0.05 * (Math.random() + 0.5))
|
|
|
|
|
},
|
|
|
|
|
riskLevel: ['低', '中等', '高'][Math.floor(Math.random() * 3)],
|
|
|
|
|
volatility: ['低', '中等', '高'][Math.floor(Math.random() * 3)],
|
|
|
|
|
overallView: ['观望', '中线', '多头排列', '空头排列', '震荡'][Math.floor(Math.random() * 5)],
|
|
|
|
|
aiAnalysis: `MACD:金叉向上 | RSI:${Math.floor(Math.random() * 80) + 10}(中性) | 布林带:中轨附近`
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`获取品种${symbol}详情失败:`, error);
|
|
|
|
|
// 失败时使用模拟数据
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
|
const future = futuresList.find(item => item.code === symbol);
|
|
|
|
|
if (!future) {
|
|
|
|
|
throw new Error('品种不存在');
|
|
|
|
|
}
|
|
|
|
|
return generateFutureData(symbol, future.name);
|
|
|
|
|
}
|
|
|
|
|
return generateFutureData(symbol, future.name);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取K线数据
|
|
|
|
|
export const fetchKlineData = async (symbol: string, period: string) => {
|
|
|
|
|
// 模拟API请求延迟
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
|
return generateKlineData(30);
|
|
|
|
|
try {
|
|
|
|
|
// 获取数据源配置
|
|
|
|
|
const dataSourceConfig = getDataSourceConfig();
|
|
|
|
|
// 使用TQSDK数据源
|
|
|
|
|
const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK, dataSourceConfig);
|
|
|
|
|
const klineData = await dataSource.getKlineData(symbol, period, 30);
|
|
|
|
|
|
|
|
|
|
// 转换为前端需要的格式
|
|
|
|
|
return klineData.map(item => ({
|
|
|
|
|
timestamp: item.datetime / 1000000000, // 转换为秒
|
|
|
|
|
open: item.open,
|
|
|
|
|
high: item.high,
|
|
|
|
|
low: item.low,
|
|
|
|
|
close: item.close,
|
|
|
|
|
volume: item.volume
|
|
|
|
|
}));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`获取合约${symbol}K线数据失败:`, error);
|
|
|
|
|
// 失败时使用模拟数据
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
|
return generateKlineData(30);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取市场热点
|
|
|
|
|
export const fetchMarketHotspots = async () => {
|
|
|
|
|
// 模拟API请求延迟
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
|
const overview = generateFuturesOverview();
|
|
|
|
|
// 按涨跌幅排序,返回前10个
|
|
|
|
|
return overview
|
|
|
|
|
.sort((a, b) => Math.abs(b.changePercent) - Math.abs(a.changePercent))
|
|
|
|
|
.slice(0, 10)
|
|
|
|
|
.map(item => ({
|
|
|
|
|
symbol: item.code,
|
|
|
|
|
name: item.name,
|
|
|
|
|
change: item.changePercent,
|
|
|
|
|
volume: Math.floor(Math.random() * 1000000) + 100000
|
|
|
|
|
}));
|
|
|
|
|
try {
|
|
|
|
|
// 使用TQSDK数据源
|
|
|
|
|
const dataSource = await DataSourceFactory.getDataSource(DataSourceType.TQSDK);
|
|
|
|
|
const overview = await dataSource.getMarketOverview();
|
|
|
|
|
|
|
|
|
|
// 按涨跌幅排序,返回前10个
|
|
|
|
|
return overview
|
|
|
|
|
.sort((a, b) => Math.abs(b.change_percent) - Math.abs(a.change_percent))
|
|
|
|
|
.slice(0, 10)
|
|
|
|
|
.map(item => ({
|
|
|
|
|
symbol: item.symbol,
|
|
|
|
|
name: item.name,
|
|
|
|
|
change: item.change_percent,
|
|
|
|
|
volume: item.volume
|
|
|
|
|
}));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取市场热点失败:', error);
|
|
|
|
|
// 失败时使用模拟数据
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
|
const overview = generateFuturesOverview();
|
|
|
|
|
// 按涨跌幅排序,返回前10个
|
|
|
|
|
return overview
|
|
|
|
|
.sort((a, b) => Math.abs(b.changePercent) - Math.abs(a.changePercent))
|
|
|
|
|
.slice(0, 10)
|
|
|
|
|
.map(item => ({
|
|
|
|
|
symbol: item.code,
|
|
|
|
|
name: item.name,
|
|
|
|
|
change: item.changePercent,
|
|
|
|
|
volume: Math.floor(Math.random() * 1000000) + 100000
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取风险预警
|
|
|
|
|
export const fetchRiskAlerts = async () => {
|
|
|
|
|
// 模拟API请求延迟
|
|
|
|
|
// 直接返回模拟数据
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
|
return riskAlerts;
|
|
|
|
|
};
|