|
|
@ -12,7 +12,7 @@ let qxnlzhqEchartsRef = ref(null); |
|
|
|
let qxnlzhqEchartsInstance = null; |
|
|
|
|
|
|
|
let regions = reactive([]); |
|
|
|
|
|
|
|
const dataMax=ref(null) |
|
|
|
// 设置区域名称 位置 |
|
|
|
function getNameTop(min, max, regionMiidle) { |
|
|
|
// 获取整个图表的高度 |
|
|
@ -30,14 +30,30 @@ function getNumberTop(min, max, regionMax) { |
|
|
|
|
|
|
|
// 生成图形标注(核心逻辑) |
|
|
|
const generateGraphics = (min, max) => { |
|
|
|
let hasPartialVisible = false; // 标记是否已经遇到第一个部分可见的区域 |
|
|
|
return regions.flatMap((region) => { |
|
|
|
if(!region.min || !region.max) return []; |
|
|
|
const middleY = (Number(region.min) + Number(region.max)) / 2; |
|
|
|
return [ |
|
|
|
// 区域名称(中间位置) |
|
|
|
{ |
|
|
|
const safeY = Math.max(min, Math.min(middleY, max*0.99)); |
|
|
|
// 检查区域是否完全可见 |
|
|
|
const isFullyVisible = region.min >= min && region.max <= max; |
|
|
|
// 检查区域是否部分可见 |
|
|
|
const isPartiallyVisible = (region.min < max && region.max > min) && !isFullyVisible; |
|
|
|
// 如果已经有一个部分可见的区域名称显示了,就不再显示其他部分可见的区域名称 |
|
|
|
if (isPartiallyVisible && hasPartialVisible) { |
|
|
|
return []; |
|
|
|
} |
|
|
|
// 如果是第一个部分可见的区域,设置标记 |
|
|
|
if (isPartiallyVisible) { |
|
|
|
hasPartialVisible = true; |
|
|
|
} |
|
|
|
const graphics = []; |
|
|
|
// 区域名称(中间位置) |
|
|
|
if (isFullyVisible || isPartiallyVisible) { |
|
|
|
graphics.push({ |
|
|
|
type: "text", |
|
|
|
left: '60', // 向右调整位置 |
|
|
|
top: getNameTop(min, max, middleY), |
|
|
|
left: '10%', |
|
|
|
top: getNameTop(min, max, safeY), |
|
|
|
style: { |
|
|
|
text: region.name, |
|
|
|
fill: region.fontColor, |
|
|
@ -45,11 +61,13 @@ const generateGraphics = (min, max) => { |
|
|
|
fontWeight: "bold", |
|
|
|
}, |
|
|
|
z: 3, |
|
|
|
}, |
|
|
|
// y轴数值(顶部位置) |
|
|
|
{ |
|
|
|
}); |
|
|
|
} |
|
|
|
// y轴数值(顶部位置) |
|
|
|
if (isFullyVisible) { |
|
|
|
graphics.push({ |
|
|
|
type: "text", |
|
|
|
left: '60', // 向右调整位置 |
|
|
|
left: '5%', // 向右调整位置 |
|
|
|
top: getNumberTop(min, max, region.max), |
|
|
|
// top: 100, |
|
|
|
style: { |
|
|
@ -58,8 +76,9 @@ const generateGraphics = (min, max) => { |
|
|
|
fontSize: 12, |
|
|
|
}, |
|
|
|
z: 3, |
|
|
|
}, |
|
|
|
]; |
|
|
|
}); |
|
|
|
} |
|
|
|
return graphics; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
@ -89,41 +108,41 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
min: qxnlzhqData.dd, |
|
|
|
max: qxnlzhqData.zc, |
|
|
|
name: "【情绪冰点区】", |
|
|
|
color: "#e7a5d6", |
|
|
|
fontColor: 'white', |
|
|
|
NumberColor: 'blue', |
|
|
|
color: "#FF9F9F", |
|
|
|
fontColor: '#666666', |
|
|
|
NumberColor: 'white', |
|
|
|
}, |
|
|
|
{ |
|
|
|
min: qxnlzhqData.zc, |
|
|
|
max: qxnlzhqData.ht, |
|
|
|
name: "【认知潜伏区】", |
|
|
|
color: "#f36587", |
|
|
|
fontColor: 'white', |
|
|
|
NumberColor: 'blue', |
|
|
|
color: "#FFCB75", |
|
|
|
fontColor: '#666666', |
|
|
|
NumberColor: 'white', |
|
|
|
}, |
|
|
|
{ |
|
|
|
min: qxnlzhqData.ht, |
|
|
|
max: qxnlzhqData.qs, |
|
|
|
name: "【多空消化区】", |
|
|
|
color: "#e99883", |
|
|
|
fontColor: 'white', |
|
|
|
NumberColor: 'blue', |
|
|
|
color: "#D7E95D", |
|
|
|
fontColor: '#666666', |
|
|
|
NumberColor: 'white', |
|
|
|
}, |
|
|
|
{ |
|
|
|
min: qxnlzhqData.qs, |
|
|
|
max: qxnlzhqData.tp, |
|
|
|
name: "【共识加速区】", |
|
|
|
color: "#f0db84", |
|
|
|
fontColor: 'white', |
|
|
|
NumberColor: 'red', |
|
|
|
color: "#A0F56F", |
|
|
|
fontColor: '#666666', |
|
|
|
NumberColor: 'white', |
|
|
|
}, |
|
|
|
{ |
|
|
|
min: qxnlzhqData.tp, |
|
|
|
max: qxnlzhqData.js, |
|
|
|
name: "【情绪临界区】", |
|
|
|
color: "#dbeee3", |
|
|
|
fontColor: 'red', |
|
|
|
NumberColor: 'red', |
|
|
|
color: "#87F3CD", |
|
|
|
fontColor: '#666666', |
|
|
|
NumberColor: 'white', |
|
|
|
}, |
|
|
|
]; |
|
|
|
// gg yl为-1 不绘制部分图表 |
|
|
@ -133,9 +152,9 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
min: qxnlzhqData.js, |
|
|
|
max: qxnlzhqData.yl, |
|
|
|
name: "【杠杆失衡区】", |
|
|
|
color: "#9ac2d8", |
|
|
|
fontColor: 'red', |
|
|
|
NumberColor: 'red', |
|
|
|
color: "#51C3F9", |
|
|
|
fontColor: '#666666', |
|
|
|
NumberColor: 'white', |
|
|
|
}, |
|
|
|
) |
|
|
|
} |
|
|
@ -145,13 +164,21 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
min: qxnlzhqData.yl, |
|
|
|
max: qxnlzhqData.gg, |
|
|
|
name: "【情绪熔断区】", |
|
|
|
color: "#bce283", |
|
|
|
fontColor: 'red', |
|
|
|
NumberColor: 'red', |
|
|
|
color: "#D0A7FF", |
|
|
|
fontColor: '#666666', |
|
|
|
NumberColor: 'white', |
|
|
|
}, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// 计算动态的y轴范围 |
|
|
|
const priceValues = kline.flatMap(item => [item[1], item[2], item[3], item[4]]); |
|
|
|
const dataMin = Math.min(...priceValues); |
|
|
|
const dataMax = Math.max(...priceValues); |
|
|
|
// 获取最后一根K线数据 |
|
|
|
const lastKLine = mixData[mixData.length - 1]; |
|
|
|
const lastHigh = lastKLine.value[2]; // 最高价 |
|
|
|
const lastLow = lastKLine.value[3]; // 最低价 |
|
|
|
// 计算止盈止损价格 |
|
|
|
const stopProfitPrice = Number(qxnlzhqData.cc) * 1.05; // 止盈价 |
|
|
|
const stopLossPrice = Number(qxnlzhqData.cc) * 0.97; // 止损价 |
|
|
@ -257,7 +284,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
borderWidth: 1 |
|
|
|
} |
|
|
|
}, |
|
|
|
backgroundColor: 'rgba(0, 0, 0, 0.8)', |
|
|
|
backgroundColor: '#646E71', |
|
|
|
borderColor: '#fff', |
|
|
|
borderWidth: 1, |
|
|
|
padding: 10, |
|
|
@ -299,9 +326,9 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
result += `<div style="color: ${changeColor};">涨跌: ${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)} (${changePercent}%)</div>` |
|
|
|
result += `</div>` |
|
|
|
} else if (param.seriesName === '止盈线' && value !== null && value !== undefined && typeof value === 'number') { |
|
|
|
result += `<div style="color: #ff80ff; margin-bottom: 4px;">${param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #FF0000; margin-bottom: 4px;">${param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
} else if (param.seriesName === '止损线' && value !== null && value !== undefined && typeof value === 'number') { |
|
|
|
result += `<div style="color: #080bfd; margin-bottom: 4px;">${param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #001EFF; margin-bottom: 4px;">${param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
@ -373,17 +400,12 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
qxnlzhqData.dd < stopLossPrice * 0.98 |
|
|
|
? Math.floor(qxnlzhqData.dd) |
|
|
|
: Math.floor(stopLossPrice * 0.98), |
|
|
|
max: |
|
|
|
qxnlzhqData.yl > stopProfitPrice * 1.02 |
|
|
|
? Math.ceil(qxnlzhqData.yl) |
|
|
|
: Math.ceil(stopProfitPrice * 1.02), |
|
|
|
max: Math.max(Math.ceil(dataMax * 1.02), (qxnlzhqData.yl > 0 ? qxnlzhqData.yl : Math.ceil(dataMax * 1.02)), stopProfitPrice * 1.02), |
|
|
|
}, |
|
|
|
// 自定义区域名称和区域范围值 位置 |
|
|
|
graphic: generateGraphics(qxnlzhqData.dd < stopLossPrice * 0.98 |
|
|
|
? Math.floor(qxnlzhqData.dd) |
|
|
|
: Math.floor(stopLossPrice * 0.98), qxnlzhqData.yl > stopProfitPrice * 1.02 |
|
|
|
? Math.ceil(qxnlzhqData.yl) |
|
|
|
: Math.ceil(stopProfitPrice * 1.02)), |
|
|
|
: Math.floor(stopLossPrice * 0.98), Math.max(Math.ceil(dataMax * 1.02), (qxnlzhqData.yl > 0 ? qxnlzhqData.yl : Math.ceil(dataMax * 1.02)), stopProfitPrice * 1.02),), |
|
|
|
series: [ |
|
|
|
{ |
|
|
|
type: "candlestick", |
|
|
@ -399,10 +421,12 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
itemStyle: { |
|
|
|
normal: { |
|
|
|
// 阳线样式(收盘 > 开盘) |
|
|
|
color: '#14b143', // 开盘价 < 收盘价时为绿色 |
|
|
|
// color: '#14b143', // 开盘价 < 收盘价时为绿色 |
|
|
|
color: 'rgba(0,0,0,0)', |
|
|
|
color0: '#ef232a', // 开盘价 > 收盘价时为红色 |
|
|
|
borderColor: '#14b143', // 阳线边框色(绿) |
|
|
|
borderColor0: '#ef232a', // 阴线边框色(红) |
|
|
|
borderWidth: 1.5 |
|
|
|
} |
|
|
|
}, |
|
|
|
// 实现 分区域背景色 |
|
|
@ -453,7 +477,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
symbol: 'none', |
|
|
|
lineStyle: { |
|
|
|
normal: { |
|
|
|
color: '#ff80ff', // 蓝色 |
|
|
|
color: '#FF0000', // 蓝色 |
|
|
|
width: 2, |
|
|
|
type: 'solid' |
|
|
|
} |
|
|
@ -470,15 +494,16 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
label: { |
|
|
|
normal: { |
|
|
|
show: true, |
|
|
|
position: 'bottom', |
|
|
|
formatter: `{text|止盈: ${stopProfitPrice}}`, |
|
|
|
position: 'top', |
|
|
|
formatter: `{text|止盈}`, |
|
|
|
rich: { |
|
|
|
text: { |
|
|
|
color: '#820a06', |
|
|
|
color: '#FF0000', |
|
|
|
fontSize: 14, |
|
|
|
fontWeight: 'bold' |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
offset: [-20, 0] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -492,7 +517,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
symbol: 'none', |
|
|
|
lineStyle: { |
|
|
|
normal: { |
|
|
|
color: '#080bfd', // 红色 |
|
|
|
color: '#001EFF', |
|
|
|
width: 2, |
|
|
|
type: 'solid' |
|
|
|
} |
|
|
@ -510,20 +535,101 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
normal: { |
|
|
|
show: true, |
|
|
|
position: 'bottom', |
|
|
|
formatter: `{text|止损: ${stopLossPrice}}`, |
|
|
|
formatter: `{text|止损}`, |
|
|
|
rich: { |
|
|
|
text: { |
|
|
|
color: '#080bfd', |
|
|
|
color: '#001EFF', |
|
|
|
fontSize: 14, |
|
|
|
fontWeight: 'bold' |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
offset: [-20, 0] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
] |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: '最低价', |
|
|
|
type: 'line', |
|
|
|
symbol: 'none', |
|
|
|
lineStyle: { |
|
|
|
normal: { |
|
|
|
color: 'transparent', |
|
|
|
width: 0 |
|
|
|
} |
|
|
|
}, |
|
|
|
markPoint: { |
|
|
|
symbol: 'circle', |
|
|
|
symbolSize: 1, |
|
|
|
data: [ |
|
|
|
{ |
|
|
|
coord: [mixData.length - 1, mixData[mixData.length - 1].value[2]], |
|
|
|
itemStyle: { |
|
|
|
color: 'transparent' |
|
|
|
}, |
|
|
|
label: { |
|
|
|
normal: { |
|
|
|
show: true, |
|
|
|
position: 'top', |
|
|
|
formatter: `{text|${mixData[mixData.length - 1].value[2].toFixed(2)}}`, |
|
|
|
rich: { |
|
|
|
text: { |
|
|
|
color: '#001EFF', |
|
|
|
fontSize: 12, |
|
|
|
fontWeight: 'bold', |
|
|
|
textBorderColor: '#ffffff', |
|
|
|
textBorderWidth: 2, |
|
|
|
} |
|
|
|
}, |
|
|
|
offset: [20, 10] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
] |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: '最高价', |
|
|
|
type: 'line', |
|
|
|
symbol: 'none', |
|
|
|
lineStyle: { |
|
|
|
normal: { |
|
|
|
color: 'transparent', |
|
|
|
width: 0 |
|
|
|
} |
|
|
|
}, |
|
|
|
markPoint: { |
|
|
|
symbol: 'circle', |
|
|
|
symbolSize: 1, |
|
|
|
data: [ |
|
|
|
{ |
|
|
|
coord: [mixData.length - 1, mixData[mixData.length - 1].value[3]], |
|
|
|
itemStyle: { |
|
|
|
color: 'transparent' |
|
|
|
}, |
|
|
|
label: { |
|
|
|
normal: { |
|
|
|
show: true, |
|
|
|
position: 'bottom', |
|
|
|
formatter: `{text|${mixData[mixData.length - 1].value[3].toFixed(2)}}`, |
|
|
|
rich: { |
|
|
|
text: { |
|
|
|
color: '#FF0000', |
|
|
|
fontSize: 12, |
|
|
|
fontWeight: 'bold', |
|
|
|
textBorderColor: '#ffffff', |
|
|
|
textBorderWidth: 2, |
|
|
|
} |
|
|
|
}, |
|
|
|
offset: [20, -10] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
] |
|
|
|
} |
|
|
|
} |
|
|
|
], |
|
|
|
grid: { |
|
|
|
left: "7%", |
|
|
|