印度尼西亚股票多时间框架K线数据可视化页面

印度尼西亚股票多时间框架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. 多时间框架切换:支持1分钟、5分钟、15分钟、30分钟、1小时、日线、周线和月线视图
  2. 股票选择:可以查看雅加达综合指数(JCI)和印尼主要股票(如BBCA、BBRI等)
  3. 技术指标:包含MA(移动平均线)等常用技术指标
  4. 实时数据展示:显示当前价格、涨跌幅、开盘价、最高价和最低价
  5. 交互功能:支持图表缩放、平移等操作

技术实现

页面使用ECharts库绘制K线图,这是一个功能强大的数据可视化库。通过JavaScript生成模拟的K线数据(实际应用中应替换为真实的API调用)。 页面设计采用暗色主题,符合金融数据展示的常见风格,确保数据可视性并减少视觉疲劳。

如何使用

  1. 将上述代码保存为HTML文件
  2. 在浏览器中打开该文件
  3. 使用左侧面板切换不同的时间框架和股票
  4. 通过图表下方的滑块缩放和平移K线图

在实际应用中,您需要将模拟数据生成部分替换为真实的API调用,例如使用StockTV等金融数据服务商提供的接口。

相关推荐
3***g2051 小时前
如何使用Spring Boot框架整合Redis:超详细案例教程
spring boot·redis·后端
狂奔小菜鸡2 小时前
Day18 | 深入理解Object类
java·后端·java ee
未秃头的程序猿2 小时前
🔒 从单机到分布式:三大锁机制深度剖析与实战指南
java·后端
kungggyoyoyo2 小时前
TRAE中国版SOLO模式上线!我用它从0到1开发了一款AI小说编辑器
前端·vue.js·trae
ohyeah2 小时前
栈:那个“先进后出”的小可爱,其实超好用!
前端·数据结构
得物技术2 小时前
# 一、项目概览 Dragonboat 是纯 Go 实现的(multi-group)Raft 库。 为应用屏蔽 Raft 复杂性,提供易于使用的 NodeH
后端
4***14902 小时前
Rust系统工具开发实践指南
开发语言·后端·rust
心随雨下2 小时前
typescript中Triple-Slash Directives如何使用
前端·javascript·typescript
s***35302 小时前
Spring Boot3.x集成Flowable7.x(一)Spring Boot集成与设计、部署、发起、完成简单流程
java·spring boot·后端