|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
|
import { Card, Row, Col, Button, Select, Tabs, Statistic, Typography, Badge, Table } from 'antd';
|
|
|
import { ArrowUpOutlined, ArrowDownOutlined, LineChartOutlined, BarChartOutlined, PieChartOutlined } from '@ant-design/icons';
|
|
|
import { generateFutureData, generateKlineData } from '../../utils/mockData';
|
|
|
import useTheme from '../../hooks/useTheme';
|
|
|
|
|
|
// 导入Lightweight Charts
|
|
|
import { createChart } from 'lightweight-charts';
|
|
|
|
|
|
const { Title, Text } = Typography;
|
|
|
const { Option } = Select;
|
|
|
const { TabPane } = Tabs;
|
|
|
const { Column } = Table;
|
|
|
|
|
|
const Detail = () => {
|
|
|
const [selectedFuture, setSelectedFuture] = useState('MA');
|
|
|
const [futureData, setFutureData] = useState(generateFutureData('MA', '甲醇'));
|
|
|
const [timePeriod, setTimePeriod] = useState('1DAY');
|
|
|
const [chart, setChart] = useState(null);
|
|
|
const chartRef = useRef(null);
|
|
|
|
|
|
// 品种列表
|
|
|
const futuresList = [
|
|
|
{ code: 'MA', name: '甲醇' },
|
|
|
{ code: 'CU', name: '铜' },
|
|
|
{ code: 'SC', name: '原油' },
|
|
|
{ code: 'RB', name: '螺纹钢' },
|
|
|
{ code: 'P', name: '棕榈油' }
|
|
|
];
|
|
|
|
|
|
// K线数据
|
|
|
const klineData = generateKlineData(30);
|
|
|
|
|
|
// 切换品种
|
|
|
const handleFutureChange = (code) => {
|
|
|
const future = futuresList.find(f => f.code === code);
|
|
|
setSelectedFuture(code);
|
|
|
setFutureData(generateFutureData(code, future.name));
|
|
|
};
|
|
|
|
|
|
// 切换时间周期
|
|
|
const handleTimePeriodChange = (period) => {
|
|
|
setTimePeriod(period);
|
|
|
};
|
|
|
|
|
|
// 初始化K线图表
|
|
|
useEffect(() => {
|
|
|
if (chartRef.current) {
|
|
|
// 清除之前的图表
|
|
|
if (chart) {
|
|
|
chart.destroy();
|
|
|
}
|
|
|
|
|
|
// 创建新图表
|
|
|
const newChart = createChart(chartRef.current, {
|
|
|
width: chartRef.current.clientWidth,
|
|
|
height: 400,
|
|
|
layout: {
|
|
|
backgroundColor: '#ffffff',
|
|
|
textColor: '#333333'
|
|
|
},
|
|
|
grid: {
|
|
|
vertLines: {
|
|
|
color: '#f0f0f0'
|
|
|
},
|
|
|
horzLines: {
|
|
|
color: '#f0f0f0'
|
|
|
}
|
|
|
},
|
|
|
priceScale: {
|
|
|
borderColor: '#f0f0f0'
|
|
|
},
|
|
|
timeScale: {
|
|
|
borderColor: '#f0f0f0'
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 添加蜡烛图系列
|
|
|
const candleSeries = newChart.addCandlestickSeries({
|
|
|
upColor: '#52c41a',
|
|
|
downColor: '#ff4d4f',
|
|
|
borderUpColor: '#52c41a',
|
|
|
borderDownColor: '#ff4d4f',
|
|
|
wickUpColor: '#52c41a',
|
|
|
wickDownColor: '#ff4d4f'
|
|
|
});
|
|
|
|
|
|
// 添加成交量系列
|
|
|
const volumeSeries = newChart.addHistogramSeries({
|
|
|
color: '#82ca9d',
|
|
|
priceFormat: {
|
|
|
type: 'volume'
|
|
|
},
|
|
|
priceScaleId: '',
|
|
|
scaleMargins: {
|
|
|
top: 0.8, // 给蜡烛图留出空间
|
|
|
bottom: 0
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 准备数据
|
|
|
const candleData = klineData.map(item => ({
|
|
|
time: item.time,
|
|
|
open: parseFloat(item.open),
|
|
|
high: parseFloat(item.high),
|
|
|
low: parseFloat(item.low),
|
|
|
close: parseFloat(item.close)
|
|
|
}));
|
|
|
|
|
|
const volumeData = klineData.map(item => ({
|
|
|
time: item.time,
|
|
|
value: parseFloat(item.volume),
|
|
|
color: parseFloat(item.close) >= parseFloat(item.open) ? '#52c41a' : '#ff4d4f'
|
|
|
}));
|
|
|
|
|
|
// 设置数据
|
|
|
candleSeries.setData(candleData);
|
|
|
volumeSeries.setData(volumeData);
|
|
|
|
|
|
// 适配窗口大小
|
|
|
const handleResize = () => {
|
|
|
if (newChart) {
|
|
|
newChart.resize(chartRef.current.clientWidth, 400);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
|
|
setChart(newChart);
|
|
|
|
|
|
// 清理函数
|
|
|
return () => {
|
|
|
window.removeEventListener('resize', handleResize);
|
|
|
if (newChart) {
|
|
|
newChart.destroy();
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
}, []);
|
|
|
|
|
|
return (
|
|
|
<div>
|
|
|
{/* 页面头部 */}
|
|
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
|
|
<Col flex="auto">
|
|
|
<Title level={3}>{futureData.name} ({futureData.code}) 详情分析</Title>
|
|
|
<Text>{futureData.fullName}</Text>
|
|
|
</Col>
|
|
|
<Col flex="none">
|
|
|
<Select
|
|
|
defaultValue="MA"
|
|
|
style={{ width: 120, marginRight: 16 }}
|
|
|
onChange={handleFutureChange}
|
|
|
>
|
|
|
{futuresList.map(future => (
|
|
|
<Option key={future.code} value={future.code}>{future.name}</Option>
|
|
|
))}
|
|
|
</Select>
|
|
|
</Col>
|
|
|
</Row>
|
|
|
|
|
|
{/* 品种基本信息 */}
|
|
|
<Card style={{ marginBottom: 24 }}>
|
|
|
<Row gutter={[16, 16]}>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic
|
|
|
title="当前价格"
|
|
|
value={futureData.currentPrice}
|
|
|
valueStyle={{ color: parseFloat(futureData.changePercent) > 0 ? '#52c41a' : '#ff4d4f' }}
|
|
|
suffix="元"
|
|
|
/>
|
|
|
</Col>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic
|
|
|
title="涨跌幅"
|
|
|
value={futureData.changePercent}
|
|
|
valueStyle={{ color: parseFloat(futureData.changePercent) > 0 ? '#52c41a' : '#ff4d4f' }}
|
|
|
suffix="%"
|
|
|
/>
|
|
|
</Col>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic title="胜率" value={futureData.winRate} suffix="%" />
|
|
|
</Col>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic title="ATR" value={futureData.atr} />
|
|
|
</Col>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic title="ADX" value={futureData.adx} />
|
|
|
</Col>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic title="趋势状态" value={futureData.adxStatus} />
|
|
|
</Col>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic title="风险等级" value={futureData.riskLevel} />
|
|
|
</Col>
|
|
|
<Col xs={24} sm={12} md={8} lg={6}>
|
|
|
<Statistic title="波动率" value={futureData.volatility} />
|
|
|
</Col>
|
|
|
</Row>
|
|
|
</Card>
|
|
|
|
|
|
{/* K线图表区 */}
|
|
|
<Card title="K线图表" style={{ marginBottom: 24 }}>
|
|
|
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
|
<div>
|
|
|
<Select
|
|
|
defaultValue="1DAY"
|
|
|
style={{ width: 120 }}
|
|
|
onChange={handleTimePeriodChange}
|
|
|
>
|
|
|
<Option value="5MIN">5分钟</Option>
|
|
|
<Option value="30MIN">30分钟</Option>
|
|
|
<Option value="1HOUR">1小时</Option>
|
|
|
<Option value="1DAY">1天</Option>
|
|
|
<Option value="1WEEK">1周</Option>
|
|
|
</Select>
|
|
|
</div>
|
|
|
<div style={{ display: 'flex', gap: 8 }}>
|
|
|
<Button icon={<LineChartOutlined />}>MA</Button>
|
|
|
<Button icon={<BarChartOutlined />}>MACD</Button>
|
|
|
<Button icon={<PieChartOutlined />}>KDJ</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div ref={chartRef} style={{ width: '100%', height: 400 }} />
|
|
|
</Card>
|
|
|
|
|
|
{/* 技术指标和AI研判区 */}
|
|
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
|
|
<Col xs={24} lg={12}>
|
|
|
<Card title="技术指标">
|
|
|
<Table dataSource={Object.entries(futureData.indicators).map(([key, value]) => ({ key, value }))} rowKey="key" size="small">
|
|
|
<Column title="指标" dataIndex="key" render={(text) => {
|
|
|
const indicatorNames = {
|
|
|
macd: 'MACD',
|
|
|
rsi: 'RSI',
|
|
|
bollinger: '布林带',
|
|
|
kdj: 'KDJ'
|
|
|
};
|
|
|
return indicatorNames[text] || text;
|
|
|
}} />
|
|
|
<Column title="状态" dataIndex="value" />
|
|
|
</Table>
|
|
|
</Card>
|
|
|
</Col>
|
|
|
<Col xs={24} lg={12}>
|
|
|
<Card title="多周期趋势">
|
|
|
<Table dataSource={Object.entries(futureData.trends).map(([period, data]) => ({ period, ...data }))} rowKey="period" size="small">
|
|
|
<Column title="周期" dataIndex="period" />
|
|
|
<Column title="方向" dataIndex="direction" render={(text) => (
|
|
|
<Badge status={text === '看多' ? 'success' : 'error'} text={text} />
|
|
|
)} />
|
|
|
<Column title="状态" dataIndex="status" />
|
|
|
<Column title="RSI" dataIndex="rsi" />
|
|
|
</Table>
|
|
|
</Card>
|
|
|
</Col>
|
|
|
</Row>
|
|
|
|
|
|
{/* 交易建议和AI研判区 */}
|
|
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
|
|
<Col xs={24} lg={12}>
|
|
|
<Card title="交易建议">
|
|
|
<Row gutter={[16, 16]}>
|
|
|
<Col span={8}>
|
|
|
<Statistic title="入场价" value={futureData.tradingAdvice.entry} suffix="元" />
|
|
|
</Col>
|
|
|
<Col span={8}>
|
|
|
<Statistic title="止损价" value={futureData.tradingAdvice.stopLoss} suffix="元" />
|
|
|
</Col>
|
|
|
<Col span={8}>
|
|
|
<Statistic title="目标价" value={futureData.tradingAdvice.target} suffix="元" />
|
|
|
</Col>
|
|
|
</Row>
|
|
|
<div style={{ marginTop: 16, padding: 16, backgroundColor: '#f6ffed', border: '1px solid #b7eb8f', borderRadius: 4 }}>
|
|
|
<Text strong>AI建议:</Text>
|
|
|
<Text>根据多周期分析,当前{futureData.name}处于{futureData.trends[timePeriod].status},建议{futureData.trends[timePeriod].direction === '看多' ? '逢低做多' : '逢高做空'}。</Text>
|
|
|
</div>
|
|
|
</Card>
|
|
|
</Col>
|
|
|
<Col xs={24} lg={12}>
|
|
|
<Card title="AI研判">
|
|
|
<div style={{ marginBottom: 16 }}>
|
|
|
<Text strong>整体判断:</Text>
|
|
|
<Text>{futureData.name}当前处于{futureData.adxStatus},{futureData.trends[timePeriod].direction === '看多' ? '多头力量较强' : '空头力量较强'}。</Text>
|
|
|
</div>
|
|
|
<div style={{ marginBottom: 16 }}>
|
|
|
<Text strong>技术面分析:</Text>
|
|
|
<ul style={{ marginLeft: 20, marginTop: 8 }}>
|
|
|
<li>MACD:{futureData.indicators.macd}</li>
|
|
|
<li>RSI:{futureData.indicators.rsi}</li>
|
|
|
<li>布林带:{futureData.indicators.bollinger}</li>
|
|
|
<li>KDJ:{futureData.indicators.kdj}</li>
|
|
|
</ul>
|
|
|
</div>
|
|
|
<div>
|
|
|
<Text strong>风险提示:</Text>
|
|
|
<Text>当前风险等级为{futureData.riskLevel},波动率{futureData.volatility},建议控制仓位,严格设置止损。</Text>
|
|
|
</div>
|
|
|
</Card>
|
|
|
</Col>
|
|
|
</Row>
|
|
|
</div>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
export default Detail; |