diff --git a/web/app.py b/web/app.py index 1a153c0..7ee9b95 100644 --- a/web/app.py +++ b/web/app.py @@ -631,6 +631,7 @@ def analyze(): 'bi_list': [{ 'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(), 'end_time': (bi.end_klc.end_time if isinstance(bi.end_klc.end_time, str) else bi.end_klc.end_time.astimezone(client_tz).isoformat()) if bi.end_klc else None, + 'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None, 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high, 'end_price': bi.end_klc.high if convert_direction(bi.dir) == 1 else bi.end_klc.low if bi.end_klc else None, 'direction': convert_direction(bi.dir), @@ -639,6 +640,7 @@ def analyze(): 'seg_list': [{ 'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(), 'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None, + 'sure_time': format_time_safely(seg.sure_time, client_tz) if seg.sure_time else None, 'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high, 'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None, 'direction': convert_direction(seg.dir) @@ -725,6 +727,7 @@ def analyze(): result['element_bi_list'] = [{ 'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(), 'end_time': (bi.end_klc.end_time if isinstance(bi.end_klc.end_time, str) else bi.end_klc.end_time.astimezone(client_tz).isoformat()) if bi.end_klc else None, + 'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None, 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high, 'end_price': bi.end_klc.high if convert_direction(bi.dir) == 1 else bi.end_klc.low if bi.end_klc else None, 'direction': convert_direction(bi.dir), @@ -737,6 +740,7 @@ def analyze(): result['element_seg_list'] = [{ 'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(), 'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None, + 'sure_time': format_time_safely(seg.sure_time, client_tz) if seg.sure_time else None, 'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high, 'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None, 'direction': convert_direction(seg.dir) diff --git a/web/templates/index.html b/web/templates/index.html index ca2a1a1..b95bc6b 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -48,7 +48,7 @@ } .chart-container { width: 100%; - height: 600px; + height: 700px; margin-top: 10px; border: 1px solid #e9ecef; border-radius: 8px; @@ -555,6 +555,7 @@ 起始时间 结束时间 + 确认时间 起始价格 结束价格 方向 @@ -572,6 +573,7 @@ 起始时间 结束时间 + 确认时间 起始价格 结束价格 方向 @@ -979,6 +981,11 @@ $('input[name="klinePeriod"]').change(function() { console.log('K线周期切换:', $(this).attr('id'), $(this).is(':checked')); updateChartDisplay(); + + // 更新数据源信息 + if (currentData) { + setupDataSourceInfo(currentData); + } }); // 当选择不同的元素时间周期时 @@ -1134,6 +1141,14 @@ } } + // 专门用于确认时间的格式化函数,处理可能为空的情况 + function formatConfirmTime(timeStr) { + if (!timeStr || timeStr === null || timeStr === 'null' || timeStr === '') { + return '未确认'; + } + return formatTime(timeStr); + } + function formatDirection(direction) { const dirText = direction === 1 ? '向上' : '向下'; const dirClass = direction === 1 ? 'direction-up' : 'direction-down'; @@ -1149,6 +1164,12 @@ const valueClass = numValue >= 0 ? 'positive' : 'negative'; return '' + numValue.toFixed(4) + ''; } + + function formatTradePointType(type) { + const typeText = type > 0 ? `买${Math.abs(type)}` : `卖${Math.abs(type)}`; + const typeClass = type > 0 ? 'direction-up' : 'direction-down'; + return '' + typeText + ''; + } function updateChart() { // 只显示旋转加载图标 @@ -1364,24 +1385,24 @@ // 如果需要显示MACD,创建MACD容器 let macdChartContainer = null; if (showMacd) { - // 设置各图表高度 - 为三个图表分配合理比例 - mainChartContainer.style.height = '50%'; // 主图占50% - volumeChartContainer.style.top = '50%'; - volumeChartContainer.style.height = '25%'; // 成交量图占25% + // 设置各图表高度 - 为三个图表分配合理比例,主图表适度增加高度 + mainChartContainer.style.height = '55%'; // 主图占55%(约385px) + volumeChartContainer.style.top = '55%'; + volumeChartContainer.style.height = '22.5%'; // 成交量图占22.5%(约157.5px) macdChartContainer = document.createElement('div'); macdChartContainer.style.width = '100%'; - macdChartContainer.style.height = '25%'; // MACD图占25% + macdChartContainer.style.height = '22.5%'; // MACD图占22.5%(约157.5px) macdChartContainer.style.position = 'absolute'; - macdChartContainer.style.top = '75%'; // 从75%位置开始 + macdChartContainer.style.top = '77.5%'; // 从77.5%位置开始 macdChartContainer.style.left = '0'; macdChartContainer.style.right = '0'; macdChartContainer.style.borderTop = '1px solid #e0e0e0'; } else { - // 不显示MACD时的高度 - 主图和成交量图平分 - mainChartContainer.style.height = '70%'; // 主图占70% - volumeChartContainer.style.top = '70%'; - volumeChartContainer.style.height = '30%'; // 成交量图占30% + // 不显示MACD时的高度 - 主图和成交量图分配 + mainChartContainer.style.height = '72%'; // 主图占72%(约504px) + volumeChartContainer.style.top = '72%'; + volumeChartContainer.style.height = '28%'; // 成交量图占28%(约196px) } container.appendChild(mainChartContainer); @@ -4161,66 +4182,33 @@ } } - function updateTables(data) { - // K线数据表 - 始终使用主时间周期数据 - if (tables.kline) { - tables.kline.clear().destroy(); + function updateTables(currentData) { + // 检查数据有效性 + if (!currentData) { + console.error('updateTables: 传入的数据为空'); + return; } - // 检查数据是否存在且为数组 - const klineData = (data.kline_data && Array.isArray(data.kline_data)) ? data.kline_data : []; + const data = currentData; - tables.kline = $('#klineTable').DataTable({ - data: klineData, - order: [[0, 'desc']], - pageLength: 25, - columns: [ - { data: 'date', render: function(data) { return formatTime(data); } }, - { data: 'open', render: formatPrice }, - { data: 'high', render: formatPrice }, - { data: 'low', render: formatPrice }, - { data: 'close', render: formatPrice }, - { data: 'volume', render: function(data) { return parseInt(data).toLocaleString(); } } - ] - }); + // 检查用户选择的是主周期还是小周期数据 + const useElementPeriod = $('#elementPeriodKline').is(':checked'); + console.log('数据表显示周期选择:', useElementPeriod ? '小周期' : '主周期'); - // 买卖点数据表 - 优先使用元素时间周期数据 - if (tables.tradePoints) { - tables.tradePoints.clear().destroy(); - } - - // 优先使用小周期数据,如果不存在则使用主周期数据 - const tradePointsData = data.element_trade_points || data.trade_points || []; - const tradePointsSource = data.element_trade_points ? '元素周期' : '主周期'; - console.log(`表格显示${tradePointsSource}买卖点数据,共${tradePointsData.length}条`); - - tables.tradePoints = $('#tradePointsTable').DataTable({ - data: tradePointsData, - order: [[0, 'desc']], - pageLength: 25, - columns: [ - { data: 'time', render: formatTime }, - { data: 'price', render: formatPrice }, - { - data: 'type', - render: function(type) { - const typeText = type > 0 ? `买${Math.abs(type)}` : `卖${Math.abs(type)}`; - const typeClass = type > 0 ? 'direction-up' : 'direction-down'; - return '' + typeText + ''; - } - }, - { data: 'desc' } - ] - }); - - // 笔数据表 - 优先使用元素时间周期数据 + // 笔数据表更新 if (tables.bi) { tables.bi.clear().destroy(); } - // 优先使用小周期数据,如果不存在则使用主周期数据 - const biData = data.element_bi_list || data.bi_list; - const biSource = data.element_bi_list ? '元素周期' : '主周期'; + // 根据用户选择决定使用哪个周期的数据 + let biData, biSource; + if (useElementPeriod && data.element_bi_list && data.element_bi_list.length > 0) { + biData = data.element_bi_list; + biSource = '小周期'; + } else { + biData = data.bi_list; + biSource = '主周期'; + } console.log(`表格显示${biSource}笔数据,共${biData ? biData.length : 0}条`); tables.bi = $('#biTable').DataTable({ @@ -4230,6 +4218,7 @@ columns: [ { data: 'start_time', render: formatTime }, { data: 'end_time', render: formatTime }, + { data: 'sure_time', render: formatConfirmTime }, { data: 'start_price', render: formatPrice }, { data: 'end_price', render: formatPrice }, { data: 'direction', render: formatDirection }, @@ -4237,14 +4226,20 @@ ] }); - // 线段数据表 - 优先使用元素时间周期数据 + // 线段数据表更新 if (tables.seg) { tables.seg.clear().destroy(); } - // 优先使用小周期数据,如果不存在则使用主周期数据 - const segData = data.element_seg_list || data.seg_list; - const segSource = data.element_seg_list ? '元素周期' : '主周期'; + // 根据用户选择决定使用哪个周期的数据 + let segData, segSource; + if (useElementPeriod && data.element_seg_list && data.element_seg_list.length > 0) { + segData = data.element_seg_list; + segSource = '小周期'; + } else { + segData = data.seg_list; + segSource = '主周期'; + } console.log(`表格显示${segSource}线段数据,共${segData ? segData.length : 0}条`); tables.seg = $('#segTable').DataTable({ @@ -4254,20 +4249,27 @@ columns: [ { data: 'start_time', render: formatTime }, { data: 'end_time', render: formatTime }, + { data: 'sure_time', render: formatConfirmTime }, { data: 'start_price', render: formatPrice }, { data: 'end_price', render: formatPrice }, { data: 'direction', render: formatDirection } ] }); - // 中枢数据表 - 优先使用元素时间周期数据 + // 中枢数据表更新 if (tables.zs) { tables.zs.clear().destroy(); } - // 使用小周期中枢数据(如果存在) - const zsData = data.element_zs_list || data.zs_list; - const zsSource = data.element_zs_list ? '元素周期' : '主周期'; + // 根据用户选择决定使用哪个周期的数据 + let zsData, zsSource; + if (useElementPeriod && data.element_zs_list && data.element_zs_list.length > 0) { + zsData = data.element_zs_list; + zsSource = '小周期'; + } else { + zsData = data.zs_list; + zsSource = '主周期'; + } console.log(`表格显示${zsSource}中枢数据,共${zsData ? zsData.length : 0}条`); tables.zs = $('#zsTable').DataTable({ @@ -4282,14 +4284,83 @@ ] }); + // 买卖点数据表更新 + if (tables.tradePoints) { + tables.tradePoints.clear().destroy(); + } + + // 根据用户选择决定使用哪个周期的数据 + let tradePointsData, tradePointsSource; + if (useElementPeriod && data.element_trade_points && data.element_trade_points.length > 0) { + tradePointsData = data.element_trade_points; + tradePointsSource = '小周期'; + } else { + tradePointsData = data.trade_points; + tradePointsSource = '主周期'; + } + console.log(`表格显示${tradePointsSource}买卖点数据,共${tradePointsData ? tradePointsData.length : 0}条`); + + tables.tradePoints = $('#tradePointsTable').DataTable({ + data: tradePointsData || [], + order: [[0, 'desc']], + pageLength: 25, + columns: [ + { data: 'time', render: formatTime }, + { data: 'price', render: formatPrice }, + { data: 'type', render: formatTradePointType }, + { data: 'desc' } + ] + }); + + // 更新数据源信息显示 + const selectedPeriod = useElementPeriod ? '小周期' : '主周期'; + const timeframe = useElementPeriod && data.element_timeframe ? data.element_timeframe : $('#timeframe').val(); + $('#dataSourceText').html(`当前显示的是${selectedPeriod} (${timeframe}) 数据`); + + // K线数据表更新 + if (tables.kline) { + tables.kline.clear().destroy(); + } + + // 根据用户选择决定使用哪个周期的K线数据 + let klineData, klineSource; + if (useElementPeriod && data.element_kline_data && data.element_kline_data.length > 0) { + klineData = data.element_kline_data; + klineSource = '小周期'; + } else { + klineData = data.kline_data; + klineSource = '主周期'; + } + console.log(`表格显示${klineSource}K线数据,共${klineData ? klineData.length : 0}条`); + + tables.kline = $('#klineTable').DataTable({ + data: klineData || [], + order: [[0, 'desc']], + pageLength: 25, + columns: [ + { data: 'date', render: function(data) { return formatTime(data); } }, + { data: 'open', render: formatPrice }, + { data: 'high', render: formatPrice }, + { data: 'low', render: formatPrice }, + { data: 'close', render: formatPrice }, + { data: 'volume', render: function(data) { return parseInt(data).toLocaleString(); } } + ] + }); + // 未完成中枢数据表更新 if (tables.uncompletedZs) { tables.uncompletedZs.clear().destroy(); } - // 使用小周期未完成中枢数据(如果存在) - const uncompletedZsData = data.element_uncompleted_zs_list || data.uncompleted_zs_list; - const uncompletedZsSource = data.element_uncompleted_zs_list ? '元素周期' : '主周期'; + // 根据用户选择决定使用哪个周期的数据 + let uncompletedZsData, uncompletedZsSource; + if (useElementPeriod && data.element_uncompleted_zs_list && data.element_uncompleted_zs_list.length > 0) { + uncompletedZsData = data.element_uncompleted_zs_list; + uncompletedZsSource = '小周期'; + } else { + uncompletedZsData = data.uncompleted_zs_list; + uncompletedZsSource = '主周期'; + } console.log(`表格显示${uncompletedZsSource}未完成中枢数据,共${uncompletedZsData ? uncompletedZsData.length : 0}条`); tables.uncompletedZs = $('#uncompletedZsTable').DataTable({ @@ -4303,14 +4374,30 @@ ] }); - // MACD数据表 - 始终使用主K线周期数据 + // MACD数据表更新 if (tables.macd) { tables.macd.clear().destroy(); } - let macdData = []; - if (data.kline_data && Array.isArray(data.kline_data) && data.macd && data.macd.macd) { - macdData = data.kline_data.map((item, index) => { + // 根据用户选择决定使用哪个周期的MACD数据 + let macdDisplayData = []; + let macdSource; + if (useElementPeriod && data.element_kline_data && data.element_macd) { + // 使用小周期数据 + macdSource = '小周期'; + macdDisplayData = data.element_kline_data.map((item, index) => { + return { + time: item.date, + close: item.close, + macd: data.element_macd.macd[index], + signal: data.element_macd.signal[index], + histogram: data.element_macd.histogram[index] + }; + }); + } else if (data.kline_data && data.macd) { + // 使用主周期数据 + macdSource = '主周期'; + macdDisplayData = data.kline_data.map((item, index) => { return { time: item.date, close: item.close, @@ -4320,9 +4407,10 @@ }; }); } + console.log(`表格显示${macdSource}MACD数据,共${macdDisplayData.length}条`); tables.macd = $('#macdTable').DataTable({ - data: macdData, + data: macdDisplayData, order: [[0, 'desc']], pageLength: 25, columns: [ @@ -4334,48 +4422,61 @@ ] }); - // 设置数据源信息 + // 更新数据源信息 setupDataSourceInfo(data); } // 设置数据源信息显示 function setupDataSourceInfo(data) { - // 初始化数据源显示 + // 获取用户当前的周期选择 + const useElementPeriod = $('#elementPeriodKline').is(':checked'); const mainTimeframe = $('#timeframe').val(); const elementTimeframe = data.element_timeframe || mainTimeframe; // 添加标签点击事件 $('#kline-tab, #macd-tab').off('click').on('click', function() { $('.data-source-info').show(); - $('#dataSourceText').html(`当前显示的是主周期 (${mainTimeframe}) 数据`); + if (useElementPeriod && data.element_kline_data && data.element_kline_data.length > 0) { + $('#dataSourceText').html(`当前显示的是小周期 (${elementTimeframe}) 数据`); + } else { + $('#dataSourceText').html(`当前显示的是主周期 (${mainTimeframe}) 数据`); + } }); $('#bi-tab').off('click').on('click', function() { $('.data-source-info').show(); - const source = data.element_bi_list ? elementTimeframe : mainTimeframe; - const sourceType = data.element_bi_list ? '元素周期' : '主周期'; - $('#dataSourceText').html(`当前显示的是${sourceType} (${source}) 笔数据`); + if (useElementPeriod && data.element_bi_list && data.element_bi_list.length > 0) { + $('#dataSourceText').html(`当前显示的是小周期 (${elementTimeframe}) 笔数据`); + } else { + $('#dataSourceText').html(`当前显示的是主周期 (${mainTimeframe}) 笔数据`); + } }); $('#seg-tab').off('click').on('click', function() { $('.data-source-info').show(); - const source = data.element_seg_list ? elementTimeframe : mainTimeframe; - const sourceType = data.element_seg_list ? '元素周期' : '主周期'; - $('#dataSourceText').html(`当前显示的是${sourceType} (${source}) 线段数据`); + if (useElementPeriod && data.element_seg_list && data.element_seg_list.length > 0) { + $('#dataSourceText').html(`当前显示的是小周期 (${elementTimeframe}) 线段数据`); + } else { + $('#dataSourceText').html(`当前显示的是主周期 (${mainTimeframe}) 线段数据`); + } }); $('#zs-tab').off('click').on('click', function() { $('.data-source-info').show(); - const source = data.element_zs_list ? elementTimeframe : mainTimeframe; - const sourceType = data.element_zs_list ? '元素周期' : '主周期'; - $('#dataSourceText').html(`当前显示的是${sourceType} (${source}) 中枢数据`); + if (useElementPeriod && data.element_zs_list && data.element_zs_list.length > 0) { + $('#dataSourceText').html(`当前显示的是小周期 (${elementTimeframe}) 中枢数据`); + } else { + $('#dataSourceText').html(`当前显示的是主周期 (${mainTimeframe}) 中枢数据`); + } }); $('#trade-points-tab').off('click').on('click', function() { $('.data-source-info').show(); - const source = data.element_trade_points ? elementTimeframe : mainTimeframe; - const sourceType = data.element_trade_points ? '元素周期' : '主周期'; - $('#dataSourceText').html(`当前显示的是${sourceType} (${source}) 买卖点数据`); + if (useElementPeriod && data.element_trade_points && data.element_trade_points.length > 0) { + $('#dataSourceText').html(`当前显示的是小周期 (${elementTimeframe}) 买卖点数据`); + } else { + $('#dataSourceText').html(`当前显示的是主周期 (${mainTimeframe}) 买卖点数据`); + } }); // 初始触发当前标签的点击事件 @@ -4855,6 +4956,7 @@ columns: [ { data: 'start_time', render: formatTime }, { data: 'end_time', render: formatTime }, + { data: 'sure_time', render: formatConfirmTime }, { data: 'start_price', render: formatPrice }, { data: 'end_price', render: formatPrice }, { data: 'direction', render: formatDirection }, @@ -4879,6 +4981,7 @@ columns: [ { data: 'start_time', render: formatTime }, { data: 'end_time', render: formatTime }, + { data: 'sure_time', render: formatConfirmTime }, { data: 'start_price', render: formatPrice }, { data: 'end_price', render: formatPrice }, { data: 'direction', render: formatDirection }