印度尼西亚股票多时间框架K线数据可视化页面
下面我将为您创建一个直观展示印尼股票多时间框架K线数据的HTML页面,支持1分钟到1个月的不同时间周期切换。
设计思路
首先分析需求:需要展示印尼股票的K线数据,支持1分钟、5分钟、15分钟、30分钟、1小时、1日和1月等不同时间框架的切换。页面需要直观易用,包含K线图、技术指标和交互控制面板。 以下是完整的实现代码:
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>印尼股票多时间框架K线数据</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a3a 0%, #0d1b2a 100%);
color: #e0e0e0;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 20px;
padding: 20px;
background: rgba(16, 30, 44, 0.8);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
h1 {
color: #4fc3f7;
margin-bottom: 10px;
font-size: 2.2rem;
}
.subtitle {
color: #90a4ae;
font-size: 1.1rem;
}
.content {
display: flex;
gap: 20px;
}
.chart-container {
flex: 1;
background: rgba(16, 30, 44, 0.8);
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.panel {
width: 280px;
background: rgba(16, 30, 44, 0.8);
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.interval-selector {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.interval-btn {
padding: 12px 8px;
background: #1e3a5c;
border: none;
border-radius: 6px;
color: #bbdefb;
cursor: pointer;
transition: all 0.3s;
font-weight: 600;
text-align: center;
}
.interval-btn:hover {
background: #2a4d7a;
transform: translateY(-2px);
}
.interval-btn.active {
background: #1565c0;
color: white;
box-shadow: 0 2px 8px rgba(21, 101, 192, 0.4);
}
.stock-selector {
margin-bottom: 20px;
}
.stock-selector select {
width: 100%;
padding: 12px;
background: #1e3a5c;
border: none;
border-radius: 6px;
color: #e0e0e0;
font-size: 1rem;
}
.info-card {
background: #1e3a5c;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
}
.info-card h3 {
color: #4fc3f7;
margin-bottom: 10px;
font-size: 1.1rem;
}
.price {
font-size: 1.8rem;
font-weight: bold;
color: #4caf50;
margin: 10px 0;
}
.change.positive {
color: #4caf50;
}
.change.negative {
color: #f44336;
}
.stats {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 1.2rem;
font-weight: bold;
}
.stat-label {
font-size: 0.8rem;
color: #90a4ae;
}
#chart {
width: 100%;
height: 600px;
}
.time-info {
text-align: center;
margin-top: 10px;
color: #90a4ae;
font-size: 0.9rem;
}
.indicator-selector {
margin: 20px 0;
}
.indicator-btns {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.indicator-btn {
padding: 8px 12px;
background: #1a2a3a;
border: 1px solid #2a4d7a;
border-radius: 4px;
color: #bbdefb;
cursor: pointer;
transition: all 0.2s;
font-size: 0.9rem;
}
.indicator-btn.active {
background: #1565c0;
border-color: #4fc3f7;
}
footer {
text-align: center;
margin-top: 20px;
padding: 15px;
color: #90a4ae;
font-size: 0.9rem;
}
@media (max-width: 1024px) {
.content {
flex-direction: column;
}
.panel {
width: 100%;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>印度尼西亚股票多时间框架K线数据</h1>
<p class="subtitle">实时展示印尼雅加达综合指数(JCI)及主要股票的多周期K线图</p>
</header>
<div class="content">
<div class="chart-container">
<div id="chart"></div>
<div class="time-info" id="timeInfo">数据更新时间: --</div>
</div>
<div class="panel">
<div class="stock-selector">
<select id="stockSelect">
<option value="JCI">雅加达综合指数 (JCI)</option>
<option value="BBCA">中亚银行 (BBCA)</option>
<option value="BBRI">印尼人民银行 (BBRI)</option>
<option value="TLKM">印尼电信 (TLKM)</option>
<option value="UNVR">联合利华印尼 (UNVR)</option>
</select>
</div>
<div class="interval-selector">
<button class="interval-btn" data-interval="1m">1分钟</button>
<button class="interval-btn" data-interval="5m">5分钟</button>
<button class="interval-btn" data-interval="15m">15分钟</button>
<button class="interval-btn" data-interval="30m">30分钟</button>
<button class="interval-btn active" data-interval="1h">1小时</button>
<button class="interval-btn" data-interval="1d">日线</button>
<button class="interval-btn" data-interval="1w">周线</button>
<button class="interval-btn" data-interval="1mo">月线</button>
</div>
<div class="info-card">
<h3>当前价格</h3>
<div class="price" id="currentPrice">--</div>
<div class="change" id="priceChange">-- (--%)</div>
<div class="stats">
<div class="stat-item">
<div class="stat-value" id="openPrice">--</div>
<div class="stat-label">开盘</div>
</div>
<div class="stat-item">
<div class="stat-value" id="highPrice">--</div>
<div class="stat-label">最高</div>
</div>
<div class="stat-item">
<div class="stat-value" id="lowPrice">--</div>
<div class="stat-label">最低</div>
</div>
</div>
</div>
<div class="indicator-selector">
<h3>技术指标</h3>
<div class="indicator-btns">
<button class="indicator-btn active" data-indicator="ma">MA</button>
<button class="indicator-btn" data-indicator="ema">EMA</button>
<button class="indicator-btn" data-indicator="macd">MACD</button>
<button class="indicator-btn" data-indicator="rsi">RSI</button>
<button class="indicator-btn" data-indicator="boll">布林带</button>
</div>
</div>
<div class="info-card">
<h3>市场信息</h3>
<p>印尼交易所交易时间: 09:30-12:00, 13:30-16:00 (UTC+7)</p>
<p>货币: 印尼盾 (IDR)</p>
<p>主要板块: 金融、资源、电信</p>
</div>
</div>
</div>
<footer>
<p>数据来源: StockTV API | 本页面仅用于演示目的,不构成投资建议</p>
</footer>
</div>
<script>
// 初始化ECharts实例
const chartDom = document.getElementById('chart');
const myChart = echarts.init(chartDom);
// 当前选中的时间框架和股票
let currentInterval = '1h';
let currentStock = 'JCI';
let currentIndicator = 'ma';
// 模拟K线数据生成函数(实际应用中应从API获取)
function generateKLineData(interval, count = 100) {
const basePrice = 7000 + Math.random() * 1000;
const data = [];
let timestamp = new Date().getTime();
// 根据时间间隔设置时间步长(毫秒)
const intervalMs = {
'1m': 60 * 1000,
'5m': 5 * 60 * 1000,
'15m': 15 * 60 * 1000,
'30m': 30 * 60 * 1000,
'1h': 60 * 60 * 1000,
'1d': 24 * 60 * 60 * 1000,
'1w': 7 * 24 * 60 * 60 * 1000,
'1mo': 30 * 24 * 60 * 60 * 1000
}[interval] || 60 * 60 * 1000;
// 生成倒序数据(最新的数据在最后)
for (let i = count - 1; i >= 0; i--) {
const time = timestamp - i * intervalMs;
const open = i === count - 1 ? basePrice : data[data.length - 1][1];
const volatility = 10 + Math.random() * 20;
const change = (Math.random() - 0.5) * volatility;
const close = open + change;
const high = Math.max(open, close) + Math.random() * volatility / 2;
const low = Math.min(open, close) - Math.random() * volatility / 2;
const volume = Math.floor(Math.random() * 1000000) + 100000;
data.push([
echarts.format.formatTime('yyyy-MM-dd HH:mm', time),
open,
close,
low,
high,
volume
]);
}
return data;
}
// 计算移动平均线
function calculateMA(dayCount, data) {
const result = [];
for (let i = 0; i < data.length; i++) {
if (i < dayCount) {
result.push('-');
continue;
}
let sum = 0;
for (let j = 0; j < dayCount; j++) {
sum += data[i - j][2]; // 收盘价
}
result.push(+(sum / dayCount).toFixed(2));
}
return result;
}
// 更新图表
function updateChart() {
const klineData = generateKLineData(currentInterval);
// 更新信息面板
const latestData = klineData[klineData.length - 1];
document.getElementById('currentPrice').textContent = latestData[2].toLocaleString();
document.getElementById('openPrice').textContent = latestData[1].toLocaleString();
document.getElementById('highPrice').textContent = latestData[4].toLocaleString();
document.getElementById('lowPrice').textContent = latestData[3].toLocaleString();
const change = latestData[2] - latestData[1];
const changePercent = (change / latestData[1] * 100).toFixed(2);
const changeElement = document.getElementById('priceChange');
changeElement.textContent = `${change >= 0 ? '+' : ''}${change.toFixed(2)} (${changePercent}%)`;
changeElement.className = `change ${change >= 0 ? 'positive' : 'negative'}`;
// 更新时间信息
document.getElementById('timeInfo').textContent = `数据更新时间: ${new Date().toLocaleString()}`;
// 计算技术指标
const ma5 = calculateMA(5, klineData);
const ma10 = calculateMA(10, klineData);
const ma20 = calculateMA(20, klineData);
// 配置图表选项
const option = {
backgroundColor: 'transparent',
animation: false,
legend: {
top: 10,
left: 'center',
data: [currentStock, 'MA5', 'MA10', 'MA20'],
textStyle: {
color: '#e0e0e0'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
borderWidth: 1,
borderColor: '#2a4d7a',
backgroundColor: 'rgba(16, 30, 44, 0.9)',
textStyle: {
color: '#e0e0e0'
},
formatter: function (params) {
const data = params[0].data;
return `
<div>时间: ${data[0]}</div>
<div>开盘: ${data[1]}</div>
<div>收盘: ${data[2]}</div>
<div>最低: ${data[3]}</div>
<div>最高: ${data[4]}</div>
<div>成交量: ${data[5].toLocaleString()}</div>
`;
}
},
grid: {
top: 80,
left: 50,
right: 50,
bottom: 80
},
xAxis: {
type: 'category',
data: klineData.map(item => item[0]),
scale: true,
boundaryGap: false,
axisLine: {onZero: false},
splitLine: {show: false},
splitNumber: 20,
axisLabel: {
color: '#90a4ae',
formatter: function (value) {
const date = new Date(value);
if (currentInterval === '1d' || currentInterval === '1w' || currentInterval === '1mo') {
return echarts.format.formatTime('MM-dd', date);
} else {
return echarts.format.formatTime('HH:mm', date);
}
}
}
},
yAxis: {
scale: true,
splitArea: {
show: true,
areaStyle: {
color: ['rgba(255,255,255,0.02)', 'rgba(0,0,0,0.02)']
}
},
axisLabel: {
color: '#90a4ae',
formatter: function (value) {
return value.toLocaleString();
}
}
},
dataZoom: [
{
type: 'inside',
start: 50,
end: 100
},
{
show: true,
type: 'slider',
top: '90%',
height: 40,
borderColor: '#1a2a3a',
textStyle: {
color: '#90a4ae'
},
handleStyle: {
color: '#2a4d7a'
}
}
],
series: [
{
name: currentStock,
type: 'candlestick',
data: klineData,
itemStyle: {
color: '#ef5350',
color0: '#26a69a',
borderColor: '#ef5350',
borderColor0: '#26a69a'
},
markPoint: {
label: {
formatter: function (param) {
return param != null ? Math.round(param.value) : '';
}
},
data: [
{
name: '最高值',
type: 'max',
valueDim: 'highest'
},
{
name: '最低值',
type: 'min',
valueDim: 'lowest'
}
]
}
},
{
name: 'MA5',
type: 'line',
data: ma5,
smooth: true,
showSymbol: false,
lineStyle: {
width: 1,
color: '#4fc3f7'
}
},
{
name: 'MA10',
type: 'line',
data: ma10,
smooth: true,
showSymbol: false,
lineStyle: {
width: 1,
color: '#fdd835'
}
},
{
name: 'MA20',
type: 'line',
data: ma20,
smooth: true,
showSymbol: false,
lineStyle: {
width: 1,
color: '#ab47bc'
}
},
{
name: '成交量',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
data: klineData.map(item => item[5]),
itemStyle: {
color: function(params) {
const data = klineData[params.dataIndex];
return data[2] >= data[1] ? '#26a69a' : '#ef5350';
}
}
}
]
};
// 设置图表选项
myChart.setOption(option);
}
// 初始化事件监听
function initEventListeners() {
// 时间框架切换
document.querySelectorAll('.interval-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.interval-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
currentInterval = this.getAttribute('data-interval');
updateChart();
});
});
// 股票选择
document.getElementById('stockSelect').addEventListener('change', function() {
currentStock = this.value;
updateChart();
});
// 技术指标切换
document.querySelectorAll('.indicator-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.indicator-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
currentIndicator = this.getAttribute('data-indicator');
// 这里可以添加切换技术指标的逻辑
});
});
// 窗口大小变化时调整图表大小
window.addEventListener('resize', function() {
myChart.resize();
});
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initEventListeners();
updateChart();
});
</script>
</body>
</html>
功能说明
这个HTML页面提供了以下功能:
- 多时间框架切换:支持1分钟、5分钟、15分钟、30分钟、1小时、日线、周线和月线视图
- 股票选择:可以查看雅加达综合指数(JCI)和印尼主要股票(如BBCA、BBRI等)
- 技术指标:包含MA(移动平均线)等常用技术指标
- 实时数据展示:显示当前价格、涨跌幅、开盘价、最高价和最低价
- 交互功能:支持图表缩放、平移等操作
技术实现
页面使用ECharts库绘制K线图,这是一个功能强大的数据可视化库。通过JavaScript生成模拟的K线数据(实际应用中应替换为真实的API调用)。 页面设计采用暗色主题,符合金融数据展示的常见风格,确保数据可视性并减少视觉疲劳。
如何使用
- 将上述代码保存为HTML文件
- 在浏览器中打开该文件
- 使用左侧面板切换不同的时间框架和股票
- 通过图表下方的滑块缩放和平移K线图
在实际应用中,您需要将模拟数据生成部分替换为真实的API调用,例如使用StockTV等金融数据服务商提供的接口。