From 68ecb4343eeb986ddf01c75153bb12c578ba5ce6 Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Fri, 27 Jun 2025 02:40:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=BA=9B=E4=B8=9C?= =?UTF-8?q?=E8=A5=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/CEnum.py | 2 + web/app.py | 11 ++- web/templates/index.html | 206 +++++++++++++++++++++++++++++++++++---- 3 files changed, 196 insertions(+), 23 deletions(-) diff --git a/Common/CEnum.py b/Common/CEnum.py index d2537ab..9daf42b 100644 --- a/Common/CEnum.py +++ b/Common/CEnum.py @@ -9,6 +9,7 @@ class DATA_SRC(Enum): class KL_TYPE(Enum): + K_1S = auto() K_1M = auto() K_DAY = auto() K_WEEK = auto() @@ -18,6 +19,7 @@ class KL_TYPE(Enum): K_15M = auto() K_30M = auto() K_60M = auto() + K_2H = auto() K_4H = auto() K_6H = auto() K_8H = auto() diff --git a/web/app.py b/web/app.py index 3633a62..c22ff99 100644 --- a/web/app.py +++ b/web/app.py @@ -127,7 +127,7 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time= batch_size = 1200 # 小时级数据可以获取更多 else: batch_size = 1500 # 日线及以上可以获取更多 - + batch_size = 1000 # 初始化存储所有K线数据的列表 all_ohlcv = [] @@ -370,13 +370,14 @@ def analyze_chan(df, timeframe='1d'): "divergence_rate": float("inf"), "bsp2_follow_1": False, "bsp3_follow_1": False, - "min_zs_cnt": 0, + "min_zs_cnt": 1, "bs1_peak": False, "macd_algo": "peak", "bs_type": '1,2,3a,1p,2s,3b', "print_warning": True, # 开启警告以便调试 "zs_algo": "normal", # 使用标准中枢算法提高稳定性 over_seg, normal, auto "bi_algo": "normal", + "seg_algo": "chan", }) # 将数据设置到WebDataAPI from DataAPI.WebDataAPI import WebDataAPI @@ -471,8 +472,10 @@ def analyze_chan(df, timeframe='1d'): # 只在数据量较少时打印分析结果摘要,避免在回放时产生过多日志 if len(df) <= 50: print(f"chan.py分析完成: 笔{len(bi_list)}个, 线段{len(seg_list)}个, 中枢{len(zs_list)}个, 买卖点{len(buy_sell_points)}个") - - + #last_seg = seg_list[-2] + #last_bi = last_seg.end_bi + #last_klu = last_bi.get_end_klu() + #print(last_klu.time, last_seg.is_sure, last_seg.dir, "--------------------------------") return { 'klc_list': kline_list.lst if hasattr(kline_list, 'lst') else [], 'klu_list': [], # KLU数据在klc中 diff --git a/web/templates/index.html b/web/templates/index.html index 4faf105..43dfd69 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -1221,6 +1221,26 @@ // 只显示旋转加载图标 $('#refreshLoadingSpinner').show(); + // 在开始更新前保存当前所有图表的视图状态 + let savedViewState = null; + if (tvWidget && tvWidget.state && tvWidget.state.isInitialized) { + savedViewState = { + visibleRange: null, + logicalRange: null + }; + + // 保存主图表的视图状态 + if (tvWidget.mainChart && tvWidget.mainChart.timeScale) { + try { + savedViewState.visibleRange = tvWidget.mainChart.timeScale().getVisibleRange(); + savedViewState.logicalRange = tvWidget.mainChart.timeScale().getVisibleLogicalRange(); + console.log('已保存图表视图状态:', savedViewState); + } catch (e) { + console.warn('保存图表视图状态时出错:', e); + } + } + } + // 获取参数 const dataSource = $('#dataSource').val() || 'crypto'; let symbol; @@ -1278,6 +1298,11 @@ // 保存当前数据 currentData = data; + // 将保存的视图状态传递给刷新函数 + if (savedViewState) { + data._savedViewState = savedViewState; + } + // 检查和记录服务器返回的时区 console.log('服务器返回的时区:', data.timezone || '未指定'); @@ -3401,27 +3426,73 @@ // 先恢复可视范围,避免先显示默认范围再跳转的跳跃效果 if (tvWidget.mainChart && (tvWidget.state.logicalRange || tvWidget.state.visibleRange)) { - console.log('回放模式:优先恢复视图范围以避免跳跃'); + console.log('检测到保存的视图状态,准备恢复所有图表范围'); - // 在回放模式下,图表已锁定,不设置范围 - if (isReplaying) { - // 回放模式下跳过范围设置,因为图表已锁定 - console.log('回放模式:跳过范围设置,图表已锁定'); - } else { - // 非回放模式下延迟应用 + // 在回放模式下,图表已锁定,不设置范围 + if (isReplaying) { + // 回放模式下跳过范围设置,因为图表已锁定 + console.log('回放模式:跳过范围设置,图表已锁定'); + } else { + // 非回放模式下延迟应用,确保所有图表都能正确恢复 setTimeout(() => { - if (tvWidget.state.logicalRange) { - tvWidget.mainChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); - if (tvWidget.volumeChart) tvWidget.volumeChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); - if (tvWidget.macdChart) tvWidget.macdChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); - if (tvWidget.atrChart) tvWidget.atrChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); - } else if (tvWidget.state.visibleRange) { - tvWidget.mainChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); - if (tvWidget.volumeChart) tvWidget.volumeChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); - if (tvWidget.macdChart) tvWidget.macdChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); - if (tvWidget.atrChart) tvWidget.atrChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); + try { + if (tvWidget.state.logicalRange) { + console.log('开始恢复所有图表的logicalRange:', tvWidget.state.logicalRange); + + // 恢复主图表 + tvWidget.mainChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); + console.log('✅ 主图表范围已恢复'); + + // 恢复成交量图表 + if (tvWidget.volumeChart && tvWidget.volumeChart.timeScale) { + tvWidget.volumeChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); + console.log('✅ 成交量图表范围已恢复'); + } + + // 恢复MACD图表 + if (tvWidget.macdChart && tvWidget.macdChart.timeScale) { + tvWidget.macdChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); + console.log('✅ MACD图表范围已恢复'); + } + + // 恢复ATR图表 + if (tvWidget.atrChart && tvWidget.atrChart.timeScale) { + tvWidget.atrChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange); + console.log('✅ ATR图表范围已恢复'); + } + + } else if (tvWidget.state.visibleRange) { + console.log('开始恢复所有图表的visibleRange:', tvWidget.state.visibleRange); + + // 恢复主图表 + tvWidget.mainChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); + console.log('✅ 主图表范围已恢复'); + + // 恢复成交量图表 + if (tvWidget.volumeChart && tvWidget.volumeChart.timeScale) { + tvWidget.volumeChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); + console.log('✅ 成交量图表范围已恢复'); + } + + // 恢复MACD图表 + if (tvWidget.macdChart && tvWidget.macdChart.timeScale) { + tvWidget.macdChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); + console.log('✅ MACD图表范围已恢复'); + } + + // 恢复ATR图表 + if (tvWidget.atrChart && tvWidget.atrChart.timeScale) { + tvWidget.atrChart.timeScale().setVisibleRange(tvWidget.state.visibleRange); + console.log('✅ ATR图表范围已恢复'); + } + } + + console.log('🎉 所有图表视图范围恢复完成'); + + } catch (e) { + console.error('❌ 恢复图表视图范围时出错:', e); } - }, 20); + }, 50); // 减少延迟到50ms以提高用户体验 } } @@ -3514,9 +3585,18 @@ } } - // 保存当前的可见范围到全局状态 + // 保存当前的可见范围到全局状态,确保ATR图表状态也被保存 if (tvWidget && tvWidget.state) { tvWidget.state.logicalRange = logicalRange; + // 同时尝试保存visibleRange作为备份 + try { + if (sourceChart && sourceChart.timeScale) { + tvWidget.state.visibleRange = sourceChart.timeScale().getVisibleRange(); + } + } catch (e) { + console.debug('保存visibleRange时出错:', e); + } + console.log('📝 图表状态已保存,包括ATR图表'); } } else { console.warn('⚠️ 无效的逻辑范围:', logicalRange); @@ -4749,12 +4829,33 @@ data.original_kline_data = data.kline_data; data.original_macd = data.macd; + // 提取保存的视图状态 + const savedViewState = data._savedViewState; + if (savedViewState) { + console.log('检测到保存的视图状态,将在图表更新后恢复:', savedViewState); + } + // 如果图表未初始化,则创建图表,否则更新图表数据 if (!tvWidget.state.isInitialized) { console.log('图表尚未初始化,创建新图表'); initTradingView($('#symbol').val(), $('#timeframe').val()); + + // 如果有保存的视图状态,在图表初始化后恢复 + if (savedViewState) { + setTimeout(() => { + restoreViewState(savedViewState); + }, 500); // 给图表足够时间完成初始化 + } } else { console.log('图表已初始化,进行增量更新'); + + // 如果有保存的视图状态,先将其存储到tvWidget.state中 + if (savedViewState) { + tvWidget.state.visibleRange = savedViewState.visibleRange; + tvWidget.state.logicalRange = savedViewState.logicalRange; + console.log('已将保存的视图状态存储到tvWidget.state'); + } + updateTradingViewData(); } @@ -4772,6 +4873,73 @@ } } + // 恢复所有图表的视图状态,包括ATR图表 + function restoreViewState(viewState) { + if (!viewState || !tvWidget) { + console.warn('无效的视图状态或图表对象,跳过恢复'); + return; + } + + console.log('开始恢复所有图表的视图状态:', viewState); + + try { + // 恢复主图表 + if (tvWidget.mainChart && tvWidget.mainChart.timeScale) { + if (viewState.logicalRange) { + tvWidget.mainChart.timeScale().setVisibleLogicalRange(viewState.logicalRange); + console.log('✅ 主图表视图状态已恢复 (logicalRange)'); + } else if (viewState.visibleRange) { + tvWidget.mainChart.timeScale().setVisibleRange(viewState.visibleRange); + console.log('✅ 主图表视图状态已恢复 (visibleRange)'); + } + } + + // 恢复成交量图表 + if (tvWidget.volumeChart && tvWidget.volumeChart.timeScale) { + if (viewState.logicalRange) { + tvWidget.volumeChart.timeScale().setVisibleLogicalRange(viewState.logicalRange); + console.log('✅ 成交量图表视图状态已恢复'); + } else if (viewState.visibleRange) { + tvWidget.volumeChart.timeScale().setVisibleRange(viewState.visibleRange); + console.log('✅ 成交量图表视图状态已恢复'); + } + } + + // 恢复MACD图表 + if (tvWidget.macdChart && tvWidget.macdChart.timeScale) { + if (viewState.logicalRange) { + tvWidget.macdChart.timeScale().setVisibleLogicalRange(viewState.logicalRange); + console.log('✅ MACD图表视图状态已恢复'); + } else if (viewState.visibleRange) { + tvWidget.macdChart.timeScale().setVisibleRange(viewState.visibleRange); + console.log('✅ MACD图表视图状态已恢复'); + } + } + + // 恢复ATR图表 + if (tvWidget.atrChart && tvWidget.atrChart.timeScale) { + if (viewState.logicalRange) { + tvWidget.atrChart.timeScale().setVisibleLogicalRange(viewState.logicalRange); + console.log('✅ ATR图表视图状态已恢复 (logicalRange)'); + } else if (viewState.visibleRange) { + tvWidget.atrChart.timeScale().setVisibleRange(viewState.visibleRange); + console.log('✅ ATR图表视图状态已恢复 (visibleRange)'); + } + } + + // 同时更新tvWidget.state以保持状态同步 + if (tvWidget.state) { + tvWidget.state.visibleRange = viewState.visibleRange; + tvWidget.state.logicalRange = viewState.logicalRange; + } + + console.log('🎉 所有图表视图状态恢复完成'); + + } catch (e) { + console.error('❌ 恢复图表视图状态时出错:', e); + } + } + // 绑定主周期MACD背离显示开关 $('#showMainMacdDiv').change(function() { refreshChartOnly();