diff --git a/ChanBI.py b/ChanBI.py index f5ef16d..19aaee3 100644 --- a/ChanBI.py +++ b/ChanBI.py @@ -4,7 +4,7 @@ from ChanEnum import Chan_BI_DIR class ChanBI(): def __init__(self, klc: ChanKLC, index, ddir=Chan_BI_DIR.UP): self.start_klc = klc - self.end_klc = None + self.end_klc = klc self.next = None self.pre = None self.dir = ddir @@ -15,7 +15,7 @@ class ChanBI(): self.sure_time = None self.klc_list = [] self.klc_list.append(klc) - self.end_time = None + self.end_time = klc.end_time self.start_time = klc.start_time self.macd_hist = 0 self.macd_div = 0 @@ -112,6 +112,9 @@ class ChanBI(): if not added: self.klc_list.append(klc) #print(self.start_time, klc.start_time) + #print(klc.end_time, klc.index) + self.end_klc = klc + self.end_time = klc.klu_list[-1].time self.cal_macdhist() self.cal_macd_div() def append_klc_list(self, klc_list): diff --git a/ChanBIZS.py b/ChanBIZS.py index 17c6b85..58b1d30 100644 --- a/ChanBIZS.py +++ b/ChanBIZS.py @@ -42,6 +42,10 @@ class ChanBIZS(): def add_bi(self, bi: ChanBI): if bi: self.bi_list.append(bi) + if bi.high > self.gg: + self.gg = bi.high + if bi.low < self.dd: + self.dd = bi.low bi.set_bi_zs(self) self.classify_zs() def set_pre(self, pre): diff --git a/ChanSEG.py b/ChanSEG.py index 67eda1c..ff775ed 100644 --- a/ChanSEG.py +++ b/ChanSEG.py @@ -45,7 +45,7 @@ class ChanSEG(): self.macd_div = macd_div def set_end_bi(self, bi: ChanBI, sure_bi: ChanBI): self.end_bi = bi - if bi: + if bi and bi.is_sure: if self.dir == Chan_SEG_DIR.UP: self.high = bi.high else: @@ -57,7 +57,7 @@ class ChanSEG(): self.format_bi_list() def pre_set_end_bi(self, bi: ChanBI): self.end_bi = bi - if bi: + if bi and bi.is_sure: if self.dir == Chan_SEG_DIR.UP: self.high = bi.high else: @@ -87,6 +87,8 @@ class ChanSEG(): if len(self.bi_list) > 0: self.bi_list.append(bi) bi.set_seg(self) + self.end_time = bi.end_time + self.end_bi = bi def cal_bi_zs(self): zs_list = [] if len(self.bi_list) > 3: diff --git a/ChanZS.py b/ChanZS.py index 0643c41..4a54bed 100644 --- a/ChanZS.py +++ b/ChanZS.py @@ -63,6 +63,10 @@ class ChanZS(): self.end_time = end_time def add_klc(self, klc): self.klc_list.append(klc) + def add_seg(self, seg): + self.seg_list.append(seg) + self.end_time = seg.end_time + self.end_seg = seg def set_zg(self, zg): self.zg = zg def set_zd(self, zd): diff --git a/TF_DF.py b/TF_DF.py index 1bb8c78..28e0910 100644 --- a/TF_DF.py +++ b/TF_DF.py @@ -175,7 +175,7 @@ class TF_DF(): else: klu_state_list.append("0") return klu_state_list - def check_fx(self, klc): + def check_fx1(self, klc): if klc.pre and klc.next: if klc.high > klc.pre.high and klc.high > klc.next.high and klc.low > klc.pre.low and klc.low > klc.next.low: if klc.pre.pre and klc.next.next: @@ -192,7 +192,7 @@ class TF_DF(): #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "BOTTOM") return Chan_FX_TYPE.BOTTOM return Chan_FX_TYPE.UNKNOWN - def check_fx1(self, klc): + def check_fx(self, klc): if klc.pre and klc.next: if klc.high > klc.pre.high and klc.high > klc.next.high and klc.low > klc.pre.low and klc.low > klc.next.low: #if (klc.close > klc.ema52 or klc.next.close > klc.next.ema52) and klc.macd > 0: @@ -954,6 +954,7 @@ class TF_DF(): if fx == Chan_FX_TYPE.UNKNOWN: if len(bi_list) > 0: bi_list[-1].add_klc(klc) + klc.set_bi(bi_list[-1]) #continue if len(bi_list) > 0 and klc.end_klu: last_bi = bi_list[-1] @@ -1074,7 +1075,7 @@ class TF_DF(): #print(klc.end_time, klc.fx, bi_list[-1].dir, "Last Top Change 2") klc.set_klc_fx_type(Chan_KLC_FX.TOP2) self.check_fx_pattern(klc) - bi_list[-1].add_klc(klc) + #bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, last_bottom.start_time, "Normal TOP Found, Confirm down bi 4") # last bottom = None 初始化的时候用,其他时间不用 @@ -1189,7 +1190,7 @@ class TF_DF(): #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 2") klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2) self.check_fx_pattern(klc) - bi_list[-1].add_klc(klc) + #bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, last_top.start_time, "Normal Bottom Found, Confirm up bi 6") # last_top = None 初始化的时候用,其他时间不用 @@ -1237,6 +1238,7 @@ class TF_DF(): #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 5") #print(klc.start_time, klc.fx, "笔买点Buy 4") self.get_above_zero_bsp(klc_list) + #print(bi_list[-1].start_time, bi_list[-1].end_time, len(bi_list[-1].klc_list)) return bi_list def get_above_zero_bsp(self, klc_list): buy_bsp_list = [] @@ -1260,12 +1262,12 @@ class TF_DF(): #print(klc.end_time, "Sell BSP Found") return buy_bsp_list def check_top_fx(self, last_bottom, klc): - if (last_bottom.high > klc.pre.low or last_bottom.high > klc.next.low) and (klc.index - last_bottom.index < 10): + if (last_bottom.high > klc.pre.low or last_bottom.high > klc.next.low) and (klc.index - last_bottom.index < 100): return False return True def check_bottom_fx(self, last_top, klc): - if (last_top.low < klc.pre.high or last_top.low < klc.next.high) and (klc.index - last_top.index < 10): + if (last_top.low < klc.pre.high or last_top.low < klc.next.high) and (klc.index - last_top.index < 100): return False return True # 建议用这种方式生成笔中枢 @@ -1364,7 +1366,7 @@ class TF_DF(): zs.set_dd(min(bi_lows)) zs.bi_list = bis_for_zs bi = bis_for_zs[-1] - if bi.end_klc: + if bi.is_sure: zs.set_end_bi(bi, bi.sure_time) start_idx = start_idx + len(added_after_leave) @@ -1404,7 +1406,7 @@ class TF_DF(): break if has_leave: - if last_bi_of_zs.end_klc: + if last_bi_of_zs.is_sure: last_zs.set_end_bi(last_bi_of_zs, last_bi_of_zs.sure_time) return bi_zs_list def find_all_bsp(self, bi_list, bi_zs_list): @@ -1434,12 +1436,12 @@ class TF_DF(): # 中枢结束后的第一笔(离开笔) last_zs_bi = zs.bi_list[-1] if last_zs_bi.dir == Chan_BI_DIR.UP: - if last_zs_bi.end_klc.high <= zs.zg or (last_zs_bi.next and last_zs_bi.next.is_sure and last_zs_bi.next.end_klc.low < zs.zd): + if last_zs_bi.is_sure and last_zs_bi.end_klc.high <= zs.zg or (last_zs_bi.next and last_zs_bi.next.is_sure and last_zs_bi.next.end_klc.low < zs.zd): leave_bi = last_zs_bi.next else: leave_bi = last_zs_bi else: - if last_zs_bi.end_klc.low >= zs.zd or (last_zs_bi.next and last_zs_bi.next.is_sure and last_zs_bi.next.end_klc.high > zs.zg): + if last_zs_bi.is_sure and last_zs_bi.end_klc.low >= zs.zd or (last_zs_bi.next and last_zs_bi.next.is_sure and last_zs_bi.next.end_klc.high > zs.zg): leave_bi = last_zs_bi.next else: leave_bi = last_zs_bi @@ -1578,13 +1580,13 @@ class TF_DF(): # 确定离开笔:中枢最后一笔之后的第一笔 if last_zs_bi.dir == Chan_BI_DIR.UP: # 中枢最后一笔向上,如果没有真正离开中枢,取下一笔 - if last_zs_bi.end_klc.high <= zs.zg: + if last_zs_bi.is_sure and last_zs_bi.end_klc.high <= zs.zg: leave_bi = last_zs_bi.next else: leave_bi = last_zs_bi else: # 中枢最后一笔向下,如果没有真正离开中枢,取下一笔 - if last_zs_bi.end_klc.low >= zs.zd: + if last_zs_bi.is_sure and last_zs_bi.end_klc.low >= zs.zd: leave_bi = last_zs_bi.next else: leave_bi = last_zs_bi @@ -1853,6 +1855,8 @@ class TF_DF(): continue else: is_break = True + else: + break if is_break: break if added_after_leave: diff --git a/web/app.py b/web/app.py index 4d8918a..c94ffff 100644 --- a/web/app.py +++ b/web/app.py @@ -33,8 +33,11 @@ class TRADE_POINT_TYPE: SELL3 = -3 # 三类卖点 app = Flask(__name__) -macd_factor = 2 *6 +macd_factor = 2 smooth_factor = 1 +macd_fast_period = 12 * macd_factor +macd_slow_period = 26 * macd_factor +macd_signal_period = 9 * smooth_factor # 初始化交易所 exchange = ccxt.binance({ 'enableRateLimit': True, @@ -422,10 +425,8 @@ def get_a_stock_kl_data(symbol, timeframe, limit=100000, start_time=None, end_ti return None def add_indicators(df): - fast = 12 * macd_factor - slow = 26 * macd_factor - period = 9 * smooth_factor - macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period) + global macd_fast_period, macd_slow_period, macd_signal_period + macd = ta.MACD(df, fastperiod=macd_fast_period, slowperiod=macd_slow_period, signalperiod=macd_signal_period) df['macd'] = macd['macd'] df['macdsignal'] = macd['macdsignal'] @@ -510,10 +511,11 @@ def add_indicators(df): def calculate_macd(df): """计算MACD指标""" - exp1 = df['close'].ewm(span=12*macd_factor, adjust=False).mean() - exp2 = df['close'].ewm(span=26*macd_factor, adjust=False).mean() + global macd_fast_period, macd_slow_period, macd_signal_period + exp1 = df['close'].ewm(span=macd_fast_period, adjust=False).mean() + exp2 = df['close'].ewm(span=macd_slow_period, adjust=False).mean() macd = exp1 - exp2 - signal = macd.ewm(span=9 * smooth_factor, adjust=False).mean() + signal = macd.ewm(span=macd_signal_period, adjust=False).mean() histogram = macd - signal return { @@ -1337,17 +1339,17 @@ def analyze(): '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), 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 - } for bi in analysis_result['bi_list'] if bi.end_klc], + } for bi in analysis_result['bi_list'] if bi.is_sure], # 添加未完成笔列表 'uncompleted_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': None, # 未完成笔没有结束时间 + 'end_time': bi.end_time, # 未完成笔没有结束时间 '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': None, # 未完成笔没有结束价格 + 'end_price': bi.end_klc.low if convert_direction(bi.dir) == 1 else bi.end_klc.high, # 未完成笔没有结束价格 'direction': convert_direction(bi.dir), 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 - } for bi in analysis_result['bi_list'] if not bi.end_klc], + } for bi in analysis_result['bi_list'] if not bi.is_sure], '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, @@ -1366,7 +1368,7 @@ def analyze(): 'gg': zs.gg, 'dd': zs.dd, 'is_sure': zs.is_sure # 添加中枢是否完成的标志 - } for zs in analysis_result['zs_list'] if zs.end_klc], + } for zs in analysis_result['zs_list'] if zs.is_sure], # 添加主周期BI中枢列表(已完成) 'bi_zs_list': [{ 'start_time': ( @@ -1532,17 +1534,17 @@ def analyze(): '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), 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 - } for bi in element_analysis['bi_list'] if bi.end_klc] + } for bi in element_analysis['bi_list'] if bi.is_sure] # 添加次周期未完成笔列表 result['element_uncompleted_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': None, # 未完成笔没有结束时间 + 'end_time': bi.end_time, # 未完成笔没有结束时间 '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': None, # 未完成笔没有结束价格 + 'end_price': bi.end_klc.low if convert_direction(bi.dir) == 1 else bi.end_klc.high, # 未完成笔没有结束价格 'direction': convert_direction(bi.dir), 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 - } for bi in element_analysis['bi_list'] if not bi.end_klc] + } for bi in element_analysis['bi_list'] if not bi.is_sure] # 添加小周期K线数据 result['element_kline_data'] = clean_dataframe_for_json(element_df).to_dict('records') @@ -1672,16 +1674,16 @@ def analyze(): '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), 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 - } for bi in sub_sub_analysis['bi_list'] if bi.end_klc] + } for bi in sub_sub_analysis['bi_list'] if bi.is_sure] result['sub_sub_uncompleted_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': None, + 'end_time': bi.end_time, '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': None, + 'end_price': bi.end_klc.low if convert_direction(bi.dir) == 1 else bi.end_klc.high, 'direction': convert_direction(bi.dir), 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 - } for bi in sub_sub_analysis['bi_list'] if not bi.end_klc] + } for bi in sub_sub_analysis['bi_list'] if not bi.is_sure] # 次次周期 KLC 列表 result['sub_sub_klc_list'] = [{ 'date': klc.end_time if isinstance(klc.end_time, str) else klc.end_time.astimezone(client_tz).isoformat(), @@ -1845,6 +1847,34 @@ def search_stock(): return jsonify({'error': str(e)}) +@app.route('/api/macd_config', methods=['GET', 'POST']) +def macd_config(): + """获取或设置MACD参数""" + global macd_fast_period, macd_slow_period, macd_signal_period + if request.method == 'GET': + return jsonify({ + 'fast': macd_fast_period, + 'slow': macd_slow_period, + 'signal': macd_signal_period + }) + else: + data = request.get_json(silent=True) or {} + fast = data.get('fast') + slow = data.get('slow') + signal = data.get('signal') + if fast is not None: + macd_fast_period = int(fast) + if slow is not None: + macd_slow_period = int(slow) + if signal is not None: + macd_signal_period = int(signal) + return jsonify({ + 'fast': macd_fast_period, + 'slow': macd_slow_period, + 'signal': macd_signal_period + }) + + def get_uncompleted_seg_list(seg_list, client_tz): """获取未完成线段列表,正确处理倒数第二个和最后一个未完成线段""" uncompleted_segs = [seg for seg in seg_list if not seg.is_sure] diff --git a/web/templates/index.html b/web/templates/index.html index 060a485..1635605 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -764,6 +764,91 @@ width: 100%; height: 100%; } + + .macd-config-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0,0,0,0.5); + z-index: 10000; + display: none; + justify-content: center; + align-items: center; + } + .macd-config-content { + background: white; + border-radius: 8px; + padding: 20px; + width: 360px; + max-width: 90vw; + box-shadow: 0 4px 20px rgba(0,0,0,0.3); + } + .macd-config-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 20px; + color: #333; + } + .macd-config-form { + display: grid; + gap: 14px; + } + .macd-config-group { + display: flex; + flex-direction: column; + gap: 6px; + } + .macd-config-label { + font-size: 14px; + font-weight: 500; + color: #555; + } + .macd-config-input { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; + } + .macd-config-input:focus { + outline: none; + border-color: #0d6efd; + box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25); + } + .macd-config-actions { + display: flex; + gap: 10px; + justify-content: flex-end; + margin-top: 20px; + } + .macd-config-btn { + padding: 8px 18px; + border: none; + border-radius: 6px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; + } + .macd-config-btn-primary { + background: #0d6efd; + color: white; + } + .macd-config-btn-primary:hover { + background: #0b5ed7; + } + .macd-config-btn-secondary { + background: #f8f9fa; + color: #6c757d; + } + .macd-config-btn-secondary:hover { + background: #e9ecef; + } + .macd-config-hint { + font-size: 12px; + color: #999; + margin-top: 2px; + } @@ -854,6 +939,7 @@
+
@@ -1170,47 +1256,6 @@ // ======= 趋势筛选(币对) ======= let trendTable = null; - // 不在前端截断数据,保留完整历史,避免K线数量限制 - function trimDataInPlace(payload, maxLen = 2000) { - if (!payload || typeof payload !== 'object') return; - delete payload.original_kline_data; - delete payload.original_macd; - - // 截断大型数组,只保留最新 maxLen 条数据 - const arrayKeys = [ - 'kline_data', 'element_kline_data', 'sub_sub_kline_data', - 'bi_list', 'element_bi_list', 'sub_sub_bi_list', - 'seg_list', 'element_seg_list', 'sub_sub_seg_list', - 'zs_list', 'element_zs_list', 'sub_sub_zs_list', - 'uncompleted_bi_list', 'uncompleted_seg_list', 'uncompleted_zs_list', - 'element_uncompleted_bi_list', 'element_uncompleted_seg_list', 'element_uncompleted_zs_list', - 'bsp_list', 'element_bsp_list', 'sub_sub_bsp_list', - 'trade_points', 'element_trade_points' - ]; - for (const key of arrayKeys) { - if (Array.isArray(payload[key]) && payload[key].length > maxLen) { - payload[key] = payload[key].slice(-maxLen); - } - } - // 截断 MACD 子数组 - const macdKeys = ['macd', 'element_macd', 'sub_sub_macd']; - for (const mk of macdKeys) { - const m = payload[mk]; - if (m && typeof m === 'object') { - for (const sub of ['macd', 'signal', 'histogram']) { - if (Array.isArray(m[sub]) && m[sub].length > maxLen) { - m[sub] = m[sub].slice(-maxLen); - } - } - } - } - // 截断 ATR 数组 - for (const ak of ['atr', 'element_atr', 'sub_sub_atr']) { - if (Array.isArray(payload[ak]) && payload[ak].length > maxLen) { - payload[ak] = payload[ak].slice(-maxLen); - } - } - } let trendDetailTable = null; let trendChart = null; @@ -1783,6 +1828,56 @@ $('#showMacd').change(function() { updateChartDisplay(); }); + + function showMacdConfig() { + $.get('/api/macd_config', function(data) { + $('#macdFastPeriod').val(data.fast); + $('#macdSlowPeriod').val(data.slow); + $('#macdSignalPeriod').val(data.signal); + $('#macdConfigModal').css('display', 'flex'); + }); + } + + function hideMacdConfig() { + $('#macdConfigModal').css('display', 'none'); + } + + function resetMacdConfig() { + $('#macdFastPeriod').val(24); + $('#macdSlowPeriod').val(52); + $('#macdSignalPeriod').val(9); + } + + function saveMacdConfig() { + const fast = parseInt($('#macdFastPeriod').val()); + const slow = parseInt($('#macdSlowPeriod').val()); + const signal = parseInt($('#macdSignalPeriod').val()); + if (fast >= slow) { + alert('快线周期必须小于慢线周期'); + return; + } + if (fast < 2 || slow < 2 || signal < 2) { + alert('周期值必须大于等于2'); + return; + } + $.ajax({ + url: '/api/macd_config', + method: 'POST', + contentType: 'application/json', + data: JSON.stringify({ fast: fast, slow: slow, signal: signal }), + success: function() { + hideMacdConfig(); + updateChart(); + }, + error: function() { + alert('保存MACD参数失败'); + } + }); + } + + $(document).on('click', '#macdConfigModal', function(e) { + if (e.target === this) hideMacdConfig(); + }); // 添加原始K线复选框变更事件 $('#showOriginalKline').change(function() { @@ -1945,16 +2040,21 @@ } ensureSubSubLteElement(); }); + let _lastKlinePeriod = 'main'; function updateChartDisplay() { if (currentData) { - // 保存当前的可见逻辑范围 + // 检测K线周期是否切换 + const curPeriod = $('#subSubPeriodKline').is(':checked') ? 'subsub' : + ($('#elementPeriodKline').is(':checked') ? 'element' : 'main'); + const periodChanged = (curPeriod !== _lastKlinePeriod); + _lastKlinePeriod = curPeriod; + + // 保存当前的可见逻辑范围(周期切换时不保留,避免范围越界) let logicalRange = null; let visibleRange = null; - if (tvWidget && tvWidget.mainChart) { - // 优先使用逻辑范围,这样在缩放级别上更准确 + if (!periodChanged && tvWidget && tvWidget.mainChart) { logicalRange = tvWidget.mainChart.timeScale().getVisibleLogicalRange(); - // 备用方案,获取实际时间戳范围 visibleRange = tvWidget.mainChart.timeScale().getVisibleRange(); } @@ -2143,9 +2243,6 @@ return; } - // 截断数据以限制内存占用 - trimDataInPlace(data, 2000); - // 保存当前数据 if (currentData) { // 覆盖前断开旧引用,帮助GC尽快回收 @@ -6585,21 +6682,29 @@ // 首先同步时间轴设置 syncTimeScaleSettings(); - // 然后让主图表适应内容 - mainChart.timeScale().fitContent(); + // 显示最近 200 根K线而非全部挤压(避免K线过多时重叠) + const totalBars = candles ? candles.length : 0; + const visibleBarsCount = 200; + if (totalBars > visibleBarsCount) { + const rangeFrom = totalBars - visibleBarsCount; + const rangeTo = totalBars + 12; + mainChart.timeScale().setVisibleLogicalRange({ from: rangeFrom, to: rangeTo }); + } else { + mainChart.timeScale().fitContent(); + } // 立即同步其他图表到主图表的范围 setTimeout(() => { - const visibleRange = mainChart.timeScale().getVisibleRange(); - if (visibleRange) { - console.log('🔧 同步可见范围:', visibleRange); - volumeChart.timeScale().setVisibleRange(visibleRange); - atrChart.timeScale().setVisibleRange(visibleRange); + const logRange = mainChart.timeScale().getVisibleLogicalRange(); + if (logRange) { + console.log('🔧 同步可见范围:', logRange); + volumeChart.timeScale().setVisibleLogicalRange(logRange); + atrChart.timeScale().setVisibleLogicalRange(logRange); if (showMacd && macdChart) { - macdChart.timeScale().setVisibleRange(visibleRange); + macdChart.timeScale().setVisibleLogicalRange(logRange); } if (showMacd && chanMacdChart) { - chanMacdChart.timeScale().setVisibleRange(visibleRange); + chanMacdChart.timeScale().setVisibleLogicalRange(logRange); } console.log('🔧 时间轴同步完成'); } @@ -10461,5 +10566,34 @@
+ + +
+
+
ChanMACD 参数设置
+
+
+ + +
默认: 24 (12×2)
+
+
+ + +
默认: 52 (26×2)
+
+
+ + +
默认: 9
+
+
+
+ + + +
+
+
\ No newline at end of file