Files
Quant_Code/999.账户相关/simnow_trader/traderdata/templates/kline12.html

522 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>实时K线图</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
#kline-chart {
width: 100%;
height: 800px;
margin: 20px auto;
}
.symbol-selector {
margin: 20px;
text-align: center;
}
button {
margin: 10px;
padding: 10px;
cursor: pointer;
}
.active-symbol {
background-color: #e0e0e0;
}
body {
margin: 0;
padding: 0;
background-color: #f5f5f5;
}
</style>
</head>
<body>
<div class="symbol-selector" id="symbol-buttons">
<!-- 动态生成按钮 -->
</div>
<div id="kline-chart"></div>
<script>
let currentSymbol = null;
const socket = io();
const symbolButtons = document.getElementById('symbol-buttons');
let chart = null;
// 初始化图表
function initChart() {
if (!chart) {
chart = echarts.init(document.getElementById('kline-chart'));
}
}
// 初始化数据
fetch('/api/data')
.then(response => response.json())
.then(data => {
updateSymbolButtons(data);
if (Object.keys(data).length > 0) {
currentSymbol = Object.keys(data)[0];
updateChart(data[currentSymbol]);
}
})
.catch(error => {
console.error('Error fetching data:', error);
});
// WebSocket事件处理
socket.on('connect', () => {
console.log('Connected to server');
});
socket.on('data_update', (data) => {
updateSymbolButtons(data);
if (currentSymbol && data[currentSymbol]) {
updateChart(data[currentSymbol]);
}
});
function updateSymbolButtons(data) {
symbolButtons.innerHTML = '';
Object.keys(data).forEach(symbol => {
const button = document.createElement('button');
button.textContent = symbol;
button.onclick = () => {
currentSymbol = symbol;
updateChart(data[symbol]);
};
if (symbol === currentSymbol) {
button.classList.add('active-symbol');
}
symbolButtons.appendChild(button);
});
}
function updateChart(data) {
initChart();
// 准备数据
const dates = data.map(item => item.datetime);
const klineData = data.map(item => [
parseFloat(item.open),
parseFloat(item.close),
parseFloat(item.low),
parseFloat(item.high)
]);
const volumes = data.map(item => parseFloat(item.volume));
const ultimateValues = data.map(item => parseFloat(item.终极平滑值));
const deltaSums = data.map(item => parseFloat(item.delta累计));
const djValues = data.map(item => parseFloat(item.dj));
const deltaValues = data.map(item => parseFloat(item.delta));
// 处理POC数据将缺值替换为前一个有效值
let pocValues = data.map(item => item.POC);
let lastValidPoc = null;
pocValues = pocValues.map(value => {
if (value === '缺值') {
return lastValidPoc;
} else {
lastValidPoc = parseFloat(value);
return lastValidPoc;
}
});
// 计算120日均线
const closes = data.map(item => parseFloat(item.close));
const ma120 = calculateMA(closes, 120);
// 处理 delta 累计数据,用于标记箭头
const arrowMarks = [];
for (let i = 1; i < deltaSums.length; i++) {
if (deltaSums[i - 1] < 0 && deltaSums[i] > 0) {
// 前一个值小于0后一个值大于0标记向上箭头
arrowMarks.push({
coord: [dates[i], data[i].low - 0.1], // 标记在 K 线下方
symbol: 'path://M0,10 L5,0 L10,10 Z',
symbolSize: [10, 10],
symbolOffset: [0, 5],
itemStyle: {
color: 'red'
}
});
} else if (deltaSums[i - 1] > 0 && deltaSums[i] < 0) {
// 前一个值大于0后一个值小于0标记向下箭头
arrowMarks.push({
coord: [dates[i], data[i].high + 0.1], // 标记在 K 线上方
symbol: 'path://M0,0 L5,10 L10,0 Z',
symbolSize: [10, 10],
symbolOffset: [0, -5],
itemStyle: {
color: 'green'
}
});
}
}
// 处理 dj 数据,用于标记圆
const circleMarks = [];
for (let i = 0; i < djValues.length; i++) {
if (djValues[i] >= 10) {
// dj 大于等于 10标记向上的红色圆
circleMarks.push({
coord: [dates[i], data[i].low - 0.1], // 标记在 K 线下方
symbol: 'circle',
symbolSize: 10,
symbolOffset: [0, 5],
itemStyle: {
color: 'red'
}
});
} else if (djValues[i] <= -10) {
// dj 小于等于 -10标记向上的绿色圆
circleMarks.push({
coord: [dates[i], data[i].high + 0.1], // 标记在 K 线下方
symbol: 'circle',
symbolSize: 10,
symbolOffset: [0, 5],
itemStyle: {
color: 'green'
}
});
}
}
// 处理 delta 值数据,用于标记 “×”
const crossMarks = [];
for (let i = 0; i < deltaValues.length; i++) {
if (deltaValues[i] >= 1000) {
// delta 值大于等于 1000标记向上的红色 “×”
crossMarks.push({
coord: [dates[i], data[i].low - 0.1], // 标记在 K 线下方
symbol: 'path://M2,0 L5,4 L8,0 L10,2 L6,6 L10,10 L8,8 L5,4 L2,8 L0,6 L4,2 Z',
symbolSize: 10,
symbolOffset: [0, 5],
itemStyle: {
color: 'red'
}
});
} else if (deltaValues[i] <= -1000) {
// delta 值小于等于 -1000标记向上的绿色 “×”
crossMarks.push({
coord: [dates[i], data[i].high + 0.1], // 标记在 K 线上方
symbol: 'path://M2,0 L5,4 L8,0 L10,2 L6,6 L10,10 L8,8 L5,4 L2,8 L0,6 L4,2 Z',
symbolSize: 10,
symbolOffset: [0, -5],
itemStyle: {
color: 'green'
}
});
}
}
// 合并箭头标记、圆标记和 “×” 标记
const allMarks = arrowMarks.concat(circleMarks).concat(crossMarks);
// 配置图表选项
const option = {
title: {
text: `${currentSymbol} K线图`,
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: ['K线', '120日均线', '终极平滑值', 'POC', '成交量', 'Delta累计', 'DJ值', 'Delta值'],
top: 30
},
grid: [
{
left: '10%',
right: '8%',
height: '40%'
},
{
left: '10%',
right: '8%',
top: '50%',
height: '10%'
},
{
left: '10%',
right: '8%',
top: '60%',
height: '10%'
},
{
left: '10%',
right: '8%',
top: '70%',
height: '10%'
},
{
left: '10%',
right: '8%',
top: '80%',
height: '10%'
}
],
xAxis: [
{
type: 'category',
data: dates,
scale: true,
boundaryGap: false,
axisLine: {onZero: false},
splitLine: {show: false},
splitNumber: 20,
gridIndex: 0
},
{
type: 'category',
gridIndex: 1,
data: dates,
axisLabel: {show: false}
},
{
type: 'category',
gridIndex: 2,
data: dates,
axisLabel: {show: false}
},
{
type: 'category',
gridIndex: 3,
data: dates,
axisLabel: {show: false}
},
{
type: 'category',
gridIndex: 4,
data: dates,
axisLabel: {show: true}
}
],
yAxis: [
{
scale: true,
splitArea: {
show: true
},
gridIndex: 0
},
{
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: {show: true},
axisLine: {show: true},
splitLine: {show: false}
},
{
scale: true,
gridIndex: 2,
splitNumber: 2,
axisLabel: {show: true},
axisLine: {show: true},
splitLine: {show: false}
},
{
scale: true,
gridIndex: 3,
splitNumber: 2,
axisLabel: {show: true},
axisLine: {show: true},
splitLine: {show: false}
},
{
scale: true,
gridIndex: 4,
splitNumber: 2,
axisLabel: {show: true},
axisLine: {show: true},
splitLine: {show: false}
}
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0, 1, 2, 3, 4],
start: 50,
end: 100
},
{
show: true,
xAxisIndex: [0, 1, 2, 3, 4],
type: 'slider',
bottom: '2%',
start: 50,
end: 100
}
],
series: [
{
name: 'K线',
type: 'candlestick',
data: klineData,
itemStyle: {
color: '#ef232a',
color0: '#14b143',
borderColor: '#ef232a',
borderColor0: '#14b143'
},
// 新增:添加标记点
markPoint: {
data: allMarks
}
},
{
name: '120日均线',
type: 'line',
data: ma120,
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: '终极平滑值',
type: 'line',
data: ultimateValues,
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'POC',
type: 'line',
data: pocValues,
smooth: true,
lineStyle: {
color: '#FFD700',
width: 2,
opacity: 0.8
},
symbol: 'circle',
symbolSize: 6
},
{
name: '成交量',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
data: volumes
},
{
name: 'Delta累计',
type: 'line',
xAxisIndex: 2,
yAxisIndex: 2,
data: deltaSums,
smooth: true,
lineStyle: {
color: '#4169E1',
width: 2,
opacity: 0.8
},
markLine: {
silent: true,
data: [
{
yAxis: 0,
lineStyle: {
color: '#999',
type: 'dashed'
}
}
]
}
},
{
name: 'DJ值',
type: 'line',
xAxisIndex: 3,
yAxisIndex: 3,
data: djValues,
smooth: true,
lineStyle: {
color: '#9932CC',
width: 2,
opacity: 0.8
},
markLine: {
silent: true,
data: [
{
yAxis: 0,
lineStyle: {
color: '#999',
type: 'dashed'
}
}
]
}
},
{
name: 'Delta值',
type: 'line',
xAxisIndex: 4,
yAxisIndex: 4,
data: deltaValues,
smooth: true,
lineStyle: {
color: '#FF8C00',
width: 2,
opacity: 0.8
},
markLine: {
silent: true,
data: [
{
yAxis: 0,
lineStyle: {
color: '#999',
type: 'dashed'
}
}
]
}
}
]
};
// 使用配置项显示图表
chart.setOption(option);
}
function calculateMA(data, dayCount) {
const result = [];
for (let i = 0, len = data.length; i < len; i++) {
if (i < dayCount - 1) {
result.push('-');
continue;
}
let sum = 0;
for (let j = 0; j < dayCount; j++) {
sum += data[i - j];
}
result.push(+(sum / dayCount).toFixed(2));
}
return result;
}
// 响应窗口大小变化
window.addEventListener('resize', function() {
if (chart) {
chart.resize();
}
});
// 初始化图表
initChart();
</script>
</body>
</html>