You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
655 lines
20 KiB
655 lines
20 KiB
<template>
|
|
<view style="width: 750rpx; height: 750rpx;">
|
|
<l-echart ref="chartRef" @finished="initChart"></l-echart>
|
|
</view>
|
|
</template>
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue';
|
|
|
|
const chartRef = ref(null)
|
|
// 获取系统信息,替代 window.innerWidth
|
|
const systemInfo = uni.getSystemInfoSync()
|
|
const screenWidth = ref(systemInfo.screenWidth || 375) // 默认值 375px
|
|
|
|
// 生成30天的交易信号数据
|
|
const generateAIGoldBullData = () => {
|
|
const data = []
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
// 模拟交易信号 [索引, 买入信号, 卖出信号, 持有信号, 强度, 成交量]
|
|
const buySignal = Math.random() > 0.7 ? 1 : 0 // 30%概率买入信号
|
|
const sellSignal = Math.random() > 0.8 ? 1 : 0 // 20%概率卖出信号
|
|
const holdSignal = Math.random() > 0.5 ? 1 : 0 // 50%概率持有信号
|
|
const strength = Math.floor(Math.random() * 3) + 1 // 信号强度1-3
|
|
const volume = Math.floor(Math.random() * 2000) + 500 // 成交量500-2500
|
|
|
|
data.push([i, buySignal, sellSignal, holdSignal, strength, volume])
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 添加缺失的变量定义
|
|
var option
|
|
const AIGoldBull = ref({
|
|
JN: generateAIGoldBullData()
|
|
})
|
|
|
|
// 模拟多语言数据
|
|
const t = ref({
|
|
suoxie: 'zh',
|
|
tianxian: '天线',
|
|
feixian: '飞线',
|
|
zhoongxian: '中线',
|
|
liuxian: '流线',
|
|
Klinetext_5: 'K线5',
|
|
Klinetext_6: 'K线6',
|
|
maipan: '买盘',
|
|
maipan1: '卖盘'
|
|
})
|
|
|
|
// 生成30天的模拟K线数据 [日期, 开盘, 最高, 最低, 收盘]
|
|
const generateKLineData = () => {
|
|
const data = []
|
|
let basePrice = 2450 // 黄金基础价格
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
const date = new Date(2024, 0, i + 1).toISOString().split('T')[0]
|
|
|
|
// 模拟价格波动
|
|
const volatility = (Math.random() - 0.5) * 50 // ±25的波动
|
|
const open = basePrice + volatility
|
|
|
|
const highVolatility = Math.random() * 30 + 10 // 10-40的向上波动
|
|
const lowVolatility = Math.random() * 30 + 10 // 10-40的向下波动
|
|
|
|
const high = Math.max(open, open + highVolatility)
|
|
const low = Math.min(open, open - lowVolatility)
|
|
|
|
const closeVolatility = (Math.random() - 0.5) * 20
|
|
const close = Math.max(low, Math.min(high, open + closeVolatility))
|
|
|
|
data.push([date,
|
|
Math.round(open * 100) / 100,
|
|
Math.round(high * 100) / 100,
|
|
Math.round(low * 100) / 100,
|
|
Math.round(close * 100) / 100
|
|
])
|
|
|
|
basePrice = close // 下一天的基础价格
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成30天的成交量和技术指标数据 [日期, 成交量1, 成交量2, 指标1, 指标2, 指标3]
|
|
const generateWaveVolData = () => {
|
|
const data = []
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
const date = new Date(2024, 0, i + 1).toISOString().split('T')[0]
|
|
|
|
// 模拟成交量数据
|
|
const vol1 = Math.floor(Math.random() * 2000) + 800 // 800-2800
|
|
const vol2 = Math.floor(Math.random() * 1500) + 600 // 600-2100
|
|
|
|
// 模拟技术指标
|
|
const indicator1 = Math.floor(Math.random() * 30) + 40 // 40-70
|
|
const indicator2 = Math.floor(Math.random() * 40) + 50 // 50-90
|
|
const indicator3 = Math.floor(Math.random() * 35) + 60 // 60-95
|
|
|
|
data.push([date, vol1, vol2, indicator1, indicator2, indicator3])
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成30天的移动平均线数据 [MA5, MA10, MA20, MA30]
|
|
const generateFTLineData = () => {
|
|
const data = []
|
|
let ma5Base = 2450
|
|
let ma10Base = 2445
|
|
let ma20Base = 2440
|
|
let ma30Base = 2435
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
// 模拟移动平均线的平滑变化
|
|
ma5Base += (Math.random() - 0.5) * 10
|
|
ma10Base += (Math.random() - 0.5) * 8
|
|
ma20Base += (Math.random() - 0.5) * 6
|
|
ma30Base += (Math.random() - 0.5) * 4
|
|
|
|
data.push([
|
|
Math.round(ma5Base * 100) / 100,
|
|
Math.round(ma10Base * 100) / 100,
|
|
Math.round(ma20Base * 100) / 100,
|
|
Math.round(ma30Base * 100) / 100
|
|
])
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成模拟数据
|
|
const mockKLineData = generateKLineData()
|
|
const mockWaveVolData = generateWaveVolData()
|
|
const mockFTLineData = generateFTLineData()
|
|
|
|
// 生成RSI指标数据 (相对强弱指数)
|
|
const generateRSIData = () => {
|
|
const data = []
|
|
for (let i = 0; i < 30; i++) {
|
|
const rsi = Math.random() * 60 + 20 // RSI值在20-80之间
|
|
data.push(Math.round(rsi * 100) / 100)
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成MACD指标数据
|
|
const generateMACDData = () => {
|
|
const data = []
|
|
for (let i = 0; i < 30; i++) {
|
|
const macd = (Math.random() - 0.5) * 20 // MACD值在-10到10之间
|
|
const signal = (Math.random() - 0.5) * 15 // 信号线
|
|
const histogram = macd - signal // 柱状图
|
|
|
|
data.push([
|
|
Math.round(macd * 100) / 100,
|
|
Math.round(signal * 100) / 100,
|
|
Math.round(histogram * 100) / 100
|
|
])
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成布林带数据
|
|
const generateBollingerData = () => {
|
|
const data = []
|
|
let middleLine = 2450
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
middleLine += (Math.random() - 0.5) * 10
|
|
const upperBand = middleLine + Math.random() * 30 + 20 // 上轨
|
|
const lowerBand = middleLine - Math.random() * 30 - 20 // 下轨
|
|
|
|
data.push([
|
|
Math.round(upperBand * 100) / 100,
|
|
Math.round(middleLine * 100) / 100,
|
|
Math.round(lowerBand * 100) / 100
|
|
])
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成成交量分析数据
|
|
const generateVolumeAnalysisData = () => {
|
|
const data = []
|
|
for (let i = 0; i < 30; i++) {
|
|
const buyVolume = Math.floor(Math.random() * 1500) + 500 // 买入量
|
|
const sellVolume = Math.floor(Math.random() * 1500) + 500 // 卖出量
|
|
const netVolume = buyVolume - sellVolume // 净买入量
|
|
|
|
data.push([buyVolume, sellVolume, netVolume])
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成市场情绪数据
|
|
const generateMarketSentimentData = () => {
|
|
const sentiments = ['极度恐慌', '恐慌', '中性', '贪婪', '极度贪婪']
|
|
const data = []
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
const sentimentIndex = Math.floor(Math.random() * 100) // 0-100的情绪指数
|
|
const sentimentLabel = sentiments[Math.floor(sentimentIndex / 20)]
|
|
|
|
data.push({
|
|
date: new Date(2024, 0, i + 1).toISOString().split('T')[0],
|
|
index: sentimentIndex,
|
|
label: sentimentLabel,
|
|
fearGreedRatio: Math.random() * 100
|
|
})
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 生成重要新闻事件数据
|
|
const generateNewsEventsData = () => {
|
|
const events = [
|
|
'美联储利率决议',
|
|
'非农就业数据发布',
|
|
'通胀数据公布',
|
|
'地缘政治紧张',
|
|
'央行政策变化',
|
|
'经济数据超预期',
|
|
'市场技术突破',
|
|
'大宗商品价格波动'
|
|
]
|
|
|
|
const data = []
|
|
for (let i = 0; i < 10; i++) { // 生成10个随机事件
|
|
const randomDay = Math.floor(Math.random() * 30) + 1
|
|
const event = events[Math.floor(Math.random() * events.length)]
|
|
const impact = Math.floor(Math.random() * 5) + 1 // 影响力1-5
|
|
|
|
data.push({
|
|
date: new Date(2024, 0, randomDay).toISOString().split('T')[0],
|
|
event: event,
|
|
impact: impact,
|
|
type: Math.random() > 0.5 ? 'positive' : 'negative'
|
|
})
|
|
}
|
|
return data.sort((a, b) => new Date(a.date) - new Date(b.date))
|
|
}
|
|
|
|
// 生成价格预测数据
|
|
const generatePricePredictionData = () => {
|
|
const data = []
|
|
let currentPrice = 2450
|
|
|
|
for (let i = 0; i < 7; i++) { // 未来7天预测
|
|
const date = new Date(2024, 1, i + 1).toISOString().split('T')[0] // 2月份
|
|
|
|
// 模拟AI预测的价格区间
|
|
const prediction = currentPrice + (Math.random() - 0.5) * 100
|
|
const confidence = Math.random() * 40 + 60 // 60-100%的置信度
|
|
const upperBound = prediction + Math.random() * 50
|
|
const lowerBound = prediction - Math.random() * 50
|
|
|
|
data.push({
|
|
date: date,
|
|
predicted_price: Math.round(prediction * 100) / 100,
|
|
confidence: Math.round(confidence),
|
|
upper_bound: Math.round(upperBound * 100) / 100,
|
|
lower_bound: Math.round(lowerBound * 100) / 100
|
|
})
|
|
|
|
currentPrice = prediction
|
|
}
|
|
return data
|
|
}
|
|
|
|
// 模拟提取的绘图数据
|
|
const extractedDrawData = {
|
|
KLine20: mockKLineData,
|
|
WAVEVOL: mockWaveVolData,
|
|
FTLINE: mockFTLineData,
|
|
RSI: generateRSIData(),
|
|
MACD: generateMACDData(),
|
|
BOLLINGER: generateBollingerData(),
|
|
VOLUME_ANALYSIS: generateVolumeAnalysisData(),
|
|
MARKET_SENTIMENT: generateMarketSentimentData(),
|
|
NEWS_EVENTS: generateNewsEventsData(),
|
|
PRICE_PREDICTION: generatePricePredictionData()
|
|
}
|
|
|
|
const fnShowEcharts4 = (extractedDrawData) => {
|
|
const splitData = (b) => {
|
|
const a = JSON.parse(JSON.stringify(b))
|
|
let categoryData = []
|
|
let values = []
|
|
for (let i = 0; i < a.length; i++) {
|
|
categoryData.push(a[i].splice(0, 1)[0])
|
|
values.push(a[i])
|
|
}
|
|
return {
|
|
categoryData,
|
|
values
|
|
}
|
|
}
|
|
var bodongliang = splitData(extractedDrawData.WAVEVOL)
|
|
function bodongliangData(values, i) {
|
|
return values.map((subArray) => subArray[i])
|
|
}
|
|
function calculateMA(index, data) {
|
|
let result = []
|
|
if (data.FTLINE) {
|
|
data.FTLINE.forEach((item) => {
|
|
result.push(item[index])
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
function vwToPx(vw) {
|
|
return (screenWidth.value * vw) / 100
|
|
}
|
|
var dealData = splitData(extractedDrawData.KLine20)
|
|
var dealGnBullData = AIGoldBull.value.JN
|
|
const textEcharts = t.value
|
|
const firstLegend = computed(() => {
|
|
if (screenWidth.value < 768) {
|
|
if (textEcharts.suoxie === 'en' || textEcharts.suoxie === 'th') {
|
|
return '2%'
|
|
} else if (textEcharts.suoxie === 'kr') {
|
|
return '2%'
|
|
} else {
|
|
return '2%'
|
|
}
|
|
} else {
|
|
return textEcharts.suoxie === 'en' ||
|
|
textEcharts.suoxie === 'th' ||
|
|
textEcharts.suoxie === 'kr'
|
|
? '9%'
|
|
: '9%'
|
|
}
|
|
})
|
|
const processBarData = (data) => {
|
|
const barData = []
|
|
data.forEach((item) => {
|
|
let color
|
|
switch (item[4]) {
|
|
case 1:
|
|
color = '#13E113'
|
|
break
|
|
case 2:
|
|
color = '#FF0E00'
|
|
break
|
|
case 3:
|
|
color = '#0000FE'
|
|
break
|
|
case 4:
|
|
color = '#1397FF'
|
|
break
|
|
}
|
|
barData.push({
|
|
value: item[5],
|
|
itemStyle: {
|
|
normal: {
|
|
color: color
|
|
}
|
|
}
|
|
})
|
|
})
|
|
return { barData }
|
|
}
|
|
const { barData } = processBarData(dealGnBullData)
|
|
option = {
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'cross'
|
|
},
|
|
backgroundColor: 'rgba(119, 120, 125, 0.6)',
|
|
borderWidth: 1,
|
|
borderColor: '#77787D',
|
|
padding: 10,
|
|
textStyle: {
|
|
color: '#fff'
|
|
}
|
|
},
|
|
axisPointer: {
|
|
link: [
|
|
{
|
|
xAxisIndex: 'all'
|
|
}
|
|
],
|
|
label: {
|
|
backgroundColor: '#77787D'
|
|
}
|
|
},
|
|
toolbox: {
|
|
show: false
|
|
},
|
|
grid: [
|
|
{
|
|
left: screenWidth.value > 768 ? '10%' : '12%',
|
|
right: screenWidth.value > 768 ? '4%' : '6%',
|
|
top: screenWidth.value > 768 ? '10%' : '12%',
|
|
height: screenWidth.value > 768 ? '35%' : '34%',
|
|
containLabel: false
|
|
},
|
|
{
|
|
left: screenWidth.value > 768 ? '10%' : '12%',
|
|
right: screenWidth.value > 768 ? '4%' : '6%',
|
|
top: screenWidth.value > 768 ? '48%' : '48%',
|
|
height: screenWidth.value > 768 ? '19%' : '21%',
|
|
containLabel: false
|
|
},
|
|
{
|
|
left: screenWidth.value > 768 ? '10%' : '12%',
|
|
right: screenWidth.value > 768 ? '4%' : '6%',
|
|
top: screenWidth.value > 768 ? '70%' : '71%',
|
|
height: screenWidth.value > 768 ? '19%' : '21%',
|
|
containLabel: false
|
|
}
|
|
],
|
|
xAxis: [
|
|
{
|
|
type: 'category',
|
|
data: dealData.categoryData,
|
|
boundaryGap: true,
|
|
axisLine: { onZero: false },
|
|
splitLine: { show: false },
|
|
min: 'dataMin',
|
|
max: 'dataMax',
|
|
axisPointer: {
|
|
z: 100,
|
|
label: {
|
|
show: false // 不显示标签
|
|
}
|
|
},
|
|
axisLine: {
|
|
lineStyle: {
|
|
color: 'black'
|
|
}
|
|
}, //
|
|
axisLabel: { show: false },
|
|
axisTick: { show: false }
|
|
},
|
|
{
|
|
type: 'category',
|
|
gridIndex: 1,
|
|
data: dealData.categoryData,
|
|
boundaryGap: true,
|
|
axisPointer: {
|
|
z: 100,
|
|
label: {
|
|
show: false // 不显示标签
|
|
}
|
|
},
|
|
axisLine: { lineStyle: { color: 'black' } },
|
|
axisLabel: {
|
|
show: false,
|
|
interval: 'auto'
|
|
},
|
|
axisTick: { show: false }
|
|
},
|
|
{
|
|
type: 'category',
|
|
gridIndex: 2,
|
|
data: dealData.categoryData,
|
|
boundaryGap: true,
|
|
axisLine: { lineStyle: { color: 'black' } },
|
|
axisLabel: {
|
|
show: true,
|
|
interval: 'auto',
|
|
fontSize: screenWidth.value > 768 ? 15 : 9
|
|
},
|
|
axisTick: { show: false }
|
|
}
|
|
],
|
|
yAxis: [
|
|
{
|
|
scale: true,
|
|
gridIndex: 0,
|
|
position: 'left',
|
|
axisLabel: {
|
|
inside: false,
|
|
align: 'right',
|
|
fontSize: screenWidth.value > 768 ? 15 : 9
|
|
},
|
|
axisLine: {
|
|
show: true,
|
|
lineStyle: {
|
|
fontSize: '',
|
|
color: 'black'
|
|
}
|
|
},
|
|
axisTick: { show: false },
|
|
splitLine: { show: false }
|
|
},
|
|
{
|
|
scale: true,
|
|
gridIndex: 1,
|
|
splitNumber: 4,
|
|
min: 0,
|
|
minInterval: 1,
|
|
axisLabel: {
|
|
show: true,
|
|
fontSize: screenWidth.value > 768 ? 15 : 9,
|
|
margin: 8,
|
|
},
|
|
axisLine: { show: true, lineStyle: { color: 'black' } },
|
|
axisTick: { show: false },
|
|
splitLine: { show: true, lineStyle: { type: 'dashed' } },
|
|
boundaryGap: ['20%', '20%']
|
|
},
|
|
{
|
|
scale: true,
|
|
gridIndex: 2,
|
|
splitNumber: 2,
|
|
axisLabel: {
|
|
show: true,
|
|
fontSize: screenWidth.value > 768 ? 15 : 9
|
|
},
|
|
axisLine: { show: true, lineStyle: { color: 'black' } },
|
|
axisTick: { show: false },
|
|
splitLine: { show: false }
|
|
}
|
|
],
|
|
dataZoom: [
|
|
{
|
|
type: 'inside',
|
|
xAxisIndex: [0, 1, 2],
|
|
start: 50,
|
|
end: 100
|
|
},
|
|
{
|
|
show: true,
|
|
xAxisIndex: [0, 1, 2],
|
|
type: 'slider',
|
|
start: 50,
|
|
end: 100
|
|
}
|
|
],
|
|
series: [
|
|
{
|
|
type: 'candlestick',
|
|
name: '日K',
|
|
xAxisIndex: 0,
|
|
yAxisIndex: 0,
|
|
data: dealData.values,
|
|
itemStyle: {
|
|
normal: {
|
|
color0: 'red',
|
|
color: 'green',
|
|
borderColor0: 'red',
|
|
borderColor: 'green'
|
|
}
|
|
},
|
|
gridIndex: 1
|
|
},
|
|
{
|
|
name: '成交量',
|
|
type: 'bar',
|
|
barWidth: '70%',
|
|
xAxisIndex: 1,
|
|
yAxisIndex: 1,
|
|
data: barData,
|
|
},
|
|
// {
|
|
// name: textEcharts.feixian,
|
|
// type: 'line',
|
|
// data: calculateMA(1, extractedDrawData),
|
|
// smooth: true,
|
|
// symbol: 'none',
|
|
// xAxisIndex: 2,
|
|
// yAxisIndex: 2,
|
|
// itemStyle: {
|
|
// normal: {
|
|
// color: '#00a32e',
|
|
// lineStyle: {
|
|
// color: '#00a32e',
|
|
// width: 2,
|
|
// type: 'solid'
|
|
// }
|
|
// }
|
|
// }
|
|
// },
|
|
// {
|
|
// name: textEcharts.zhoongxian,
|
|
// type: 'line',
|
|
// data: calculateMA(2, extractedDrawData),
|
|
// smooth: true,
|
|
// symbol: 'none',
|
|
// xAxisIndex: 2,
|
|
// yAxisIndex: 2,
|
|
// itemStyle: {
|
|
// normal: {
|
|
// color: '#de0000',
|
|
// lineStyle: {
|
|
// color: '#de0000',
|
|
// width: 2,
|
|
// type: 'solid'
|
|
// }
|
|
// }
|
|
// }
|
|
// },
|
|
// {
|
|
// name: textEcharts.tianxian,
|
|
// type: 'line',
|
|
// data: calculateMA(3, extractedDrawData),
|
|
// smooth: true,
|
|
// symbol: 'none',
|
|
// xAxisIndex: 2,
|
|
// yAxisIndex: 2,
|
|
// itemStyle: {
|
|
// normal: {
|
|
// color: '#ffb300',
|
|
// lineStyle: {
|
|
// color: '#ffb300',
|
|
// width: 2,
|
|
// type: 'solid'
|
|
// }
|
|
// }
|
|
// }
|
|
// },
|
|
// {
|
|
// name: textEcharts.liuxian,
|
|
// type: 'line',
|
|
// data: calculateMA(4, extractedDrawData),
|
|
// smooth: true,
|
|
// symbol: 'none',
|
|
// xAxisIndex: 2,
|
|
// yAxisIndex: 2,
|
|
// itemStyle: {
|
|
// normal: {
|
|
// color: '#00c8ff',
|
|
// lineStyle: {
|
|
// color: '#00c8ff',
|
|
// width: 2,
|
|
// type: 'solid'
|
|
// }
|
|
// }
|
|
// }
|
|
// },
|
|
]
|
|
}
|
|
initChart()
|
|
}
|
|
|
|
// 组件挂载时初始化图表
|
|
onMounted(() => {
|
|
// 调用图表初始化函数
|
|
fnShowEcharts4(extractedDrawData)
|
|
})
|
|
|
|
|
|
// 初始化图表
|
|
const initChart = async () => {
|
|
if (!chartRef.value) return
|
|
|
|
try {
|
|
const chart = await chartRef.value.init(echarts)
|
|
chart.setOption(option)
|
|
} catch (error) {
|
|
console.error('图表初始化失败:', error)
|
|
}
|
|
}
|
|
</script>
|