diff --git a/ChanKLC.py b/ChanKLC.py index 0e9d994..35d88f4 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -49,6 +49,7 @@ class ChanKLC(): self.volume_ratio = self.volume_ratio / len(self.klus) self.volume = self.volume / len(self.klus) self.macdhist = self.macdhist / len(self.klus) + def set_next(self, klc): self.next = klc def set_pre(self, klc): @@ -1132,7 +1133,7 @@ class ChanKLC(): # ===== 分型强度特征 ===== # 添加分型强度相关特征 - features['klc_fx_strength'] = self.calculate_fx_strength() + features['klc_fx_strength'] = self.cal_fx_strength() features['klc_fx_strength_level'] = self.get_fx_strength_level() features['klc_is_strong_fx'] = 1 if self.is_strong_fx() else 0 @@ -1146,6 +1147,269 @@ class ChanKLC(): return features + def cal_fx_strength(self): + """ + 用self.pre和self.next实现分型强弱判断 + + 核心缠论原理: + - 强分型:出现在笔的末端,能够终结当前笔,标志着趋势转折 + - 弱分型:出现在笔的中间,是中继性质,笔还会继续延伸 + + 返回值: + 3: 极强分型(笔终结+强确认) + 2: 强分型(笔终结) + 1: 偏强分型(可能终结笔) + 0: 中性分型 + -1: 偏弱分型(中继特征明显) + -2: 弱分型(明显中继) + -3: 极弱分型(无效分型) + """ + # 检查是否为分型,且有前后K线数据 + if self.fx == Chan_FX_TYPE.UNKNOWN: + return 0 + if not self.pre or not self.next: + return 100 + # === 核心判断:分型在笔中的位置 === + + # 1. 检查这个分型是否能够终结当前笔 + is_bi_end = self._check_if_bi_ending_fx() + + # 2. 检查分型的后续走势确认 + post_fx_confirmation = self._check_post_fx_confirmation() + + # 3. 检查分型的标准性和强度 + fx_quality = self._check_fx_quality() + + # === 综合评分 === + base_score = 0 + + # 笔位置是最重要的判断标准 + if is_bi_end == 2: # 强烈确认笔终结 + base_score = 2 + elif is_bi_end == 1: # 可能笔终结 + base_score = 1 + elif is_bi_end == -1: # 明显中继 + base_score = -2 + elif is_bi_end == -2: # 强烈中继特征 + base_score = -3 + else: # 不确定 + base_score = 0 + + # 后续确认调整 + base_score += post_fx_confirmation + + # 分型质量调整 + base_score += fx_quality + print(self.start_time, base_score, is_bi_end, post_fx_confirmation, fx_quality) + # 限制在-3到3范围内 + return max(-3, min(3, base_score)) + + def _check_if_bi_ending_fx(self): + """ + 检查分型是否为笔终结分型 + 返回值: + 2: 强烈确认笔终结 + 1: 可能笔终结 + 0: 不确定 + -1: 明显中继 + -2: 强烈中继特征 + """ + # 检查是否有足够的后续数据来判断 + if not self.next or not hasattr(self.next, 'next'): + return 0 + + # 获取分型后的几根K线数据 + subsequent_klcs = [] + temp = self.next + for i in range(5): # 检查后续5根K线 + if temp: + subsequent_klcs.append(temp) + temp = temp.next if hasattr(temp, 'next') else None + else: + break + + if len(subsequent_klcs) < 2: + return 0 + + if self.fx == Chan_FX_TYPE.TOP: + return self._check_top_bi_ending(subsequent_klcs) + else: # BOTTOM + return self._check_bottom_bi_ending(subsequent_klcs) + + def _check_top_bi_ending(self, subsequent_klcs): + """检查顶分型是否为笔终结""" + # 强烈笔终结特征: + # 1. 后续K线持续下跌,且跌破关键位置 + # 2. 没有新的更高的高点出现 + + broken_key_levels = 0 + new_highs = 0 + downward_trend = 0 + + # 检查关键价位突破 + first_low = self.pre.low + middle_low = self.low + key_support = min(first_low, middle_low) + + for i, klc in enumerate(subsequent_klcs): + # 检查是否跌破关键支撑 + if klc.low < key_support: + broken_key_levels += 1 + + # 检查是否出现新高 + if klc.high > self.high: + new_highs += 1 + + # 检查下跌趋势 + if i > 0 and klc.close < subsequent_klcs[i-1].close: + downward_trend += 1 + + # 强烈笔终结:跌破关键位且无新高 + if broken_key_levels >= 1 and new_highs == 0 and downward_trend >= 2: + return 2 + + # 可能笔终结:部分条件满足 + if (broken_key_levels >= 1 and new_highs <= 1) or (new_highs == 0 and downward_trend >= 3): + return 1 + + # 明显中继:出现新高且未跌破关键位 + if new_highs >= 2 and broken_key_levels == 0: + return -2 + + # 中继倾向:出现新高 + if new_highs >= 1: + return -1 + + return 0 + + def _check_bottom_bi_ending(self, subsequent_klcs): + """检查底分型是否为笔终结""" + # 强烈笔终结特征: + # 1. 后续K线持续上涨,且突破关键位置 + # 2. 没有新的更低的低点出现 + + broken_key_levels = 0 + new_lows = 0 + upward_trend = 0 + + # 检查关键价位突破 + first_high = self.pre.high + middle_high = self.high + key_resistance = max(first_high, middle_high) + + for i, klc in enumerate(subsequent_klcs): + # 检查是否突破关键阻力 + if klc.high > key_resistance: + broken_key_levels += 1 + + # 检查是否出现新低 + if klc.low < self.low: + new_lows += 1 + + # 检查上涨趋势 + if i > 0 and klc.close > subsequent_klcs[i-1].close: + upward_trend += 1 + + # 强烈笔终结:突破关键位且无新低 + if broken_key_levels >= 1 and new_lows == 0 and upward_trend >= 2: + return 2 + + # 可能笔终结:部分条件满足 + if (broken_key_levels >= 1 and new_lows <= 1) or (new_lows == 0 and upward_trend >= 3): + return 1 + + # 明显中继:出现新低且未突破关键位 + if new_lows >= 2 and broken_key_levels == 0: + return -2 + + # 中继倾向:出现新低 + if new_lows >= 1: + return -1 + + return 0 + + def _check_post_fx_confirmation(self): + """ + 检查分型后的走势确认 + 返回值:-1到1的调整分数 + """ + if not self.next: + return 0 + + score = 0 + + # 检查第三根K线的确认 + third_klc = self.next + + if self.fx == Chan_FX_TYPE.TOP: + # 顶分型:第三根K线应该走弱 + middle_price = (self.high + self.low) / 2 + + if third_klc.close < middle_price: + score += 0.5 + if third_klc.low < self.pre.low: # 跌破第一根K线低点 + score += 0.5 + if third_klc.close < third_klc.open and abs(third_klc.close - third_klc.open) > abs(self.close - self.open) * 0.5: + score += 0.3 # 明显阴线 + + else: # BOTTOM + # 底分型:第三根K线应该走强 + middle_price = (self.high + self.low) / 2 + + if third_klc.close > middle_price: + score += 0.5 + if third_klc.high > self.pre.high: # 突破第一根K线高点 + score += 0.5 + if third_klc.close > third_klc.open and abs(third_klc.close - third_klc.open) > abs(self.close - self.open) * 0.5: + score += 0.3 # 明显阳线 + + return min(1, max(-1, score)) + + def _check_fx_quality(self): + """ + 检查分型本身的质量 + 返回值:-1到1的调整分数 + """ + score = 0 + + # 检查分型的标准性 + if self.fx == Chan_FX_TYPE.TOP: + # 高点突出程度 + high_diff1 = (self.high - self.pre.high) / self.high if self.high > 0 else 0 + high_diff2 = (self.high - self.next.high) / self.high if self.high > 0 else 0 + min_diff = min(high_diff1, high_diff2) + + if min_diff > 0.03: # 非常突出 + score += 0.5 + elif min_diff > 0.01: # 比较突出 + score += 0.2 + elif min_diff < 0.003: # 不够突出 + score -= 0.5 + + else: # BOTTOM + # 低点突出程度 + low_diff1 = (self.pre.low - self.low) / self.pre.low if self.pre.low > 0 else 0 + low_diff2 = (self.next.low - self.low) / self.next.low if self.next.low > 0 else 0 + min_diff = min(low_diff1, low_diff2) + + if min_diff > 0.03: # 非常突出 + score += 0.5 + elif min_diff > 0.01: # 比较突出 + score += 0.2 + elif min_diff < 0.003: # 不够突出 + score -= 0.5 + + # 检查量价配合 + avg_volume = self._calculate_average_volume(lookback=5) + if avg_volume > 0: + volume_ratio = self.volume / avg_volume + if volume_ratio > 1.5: + score += 0.3 + elif volume_ratio < 0.7: + score -= 0.2 + + return min(1, max(-1, score)) + def calculate_fx_strength(self): """ 基于专业缠论理论的分型强度评估体系 @@ -1490,7 +1754,7 @@ class ChanKLC(): 根据专业评分标准:≥80分为有效强势分型,≤40分建议忽略 """ strength = self.calculate_fx_strength() - + return "" if strength >= 80: return "极强" elif strength >= 65: @@ -1507,4 +1771,84 @@ class ChanKLC(): 判断是否为强分型 根据专业标准调整阈值为65分 """ - return self.calculate_fx_strength() >= threshold \ No newline at end of file + return self.calculate_fx_strength() >= threshold + + def _default_top_strength_judgment(self, first_info, middle_info, last_info, first_kline, middle_kline, last_kline): + """ + 顶分型默认强弱判断 + 当不满足特定强弱条件时的保底判断 + """ + # 严格的强分型判断条件 + strong_signals = 0 + + # 判断条件1:成交量显著放大(提高标准) + avg_volume = self._calculate_average_volume(lookback=5) + volume_significantly_amplified = middle_kline.volume > avg_volume * 2.0 if avg_volume > 0 else False + if volume_significantly_amplified: + strong_signals += 1 + + # 判断条件2:中间K线有长上影线(提高标准) + has_long_upper_shadow = middle_info['upper_shadow_ratio'] > 0.6 # 从0.3提高到0.6 + if has_long_upper_shadow: + strong_signals += 1 + + # 判断条件3:后续K线收盘明显偏低(更严格) + middle_range = middle_kline.high - middle_kline.low + last_close_position = (last_kline.close - middle_kline.low) / middle_range if middle_range > 0 else 0.5 + close_significantly_low = last_close_position < 0.3 # 从0.6提高到0.3 + if close_significantly_low: + strong_signals += 1 + + # 判断条件4:最后一根K线是明显的阴线且跌幅较大 + is_significant_bearish = (last_info['is_bearish'] and + last_info['body_size'] > last_info['total_range'] * 0.5) + if is_significant_bearish: + strong_signals += 1 + + # 判断条件5:跌破前一根K线重要价位 + breaks_important_level = last_kline.low < first_kline.low + if breaks_important_level: + strong_signals += 1 + + # 需要至少4个强信号才判断为强分型,否则为弱分型 + return 1 if strong_signals >= 4 else -1 + + def _default_bottom_strength_judgment(self, first_info, middle_info, last_info, first_kline, middle_kline, last_kline): + """ + 底分型默认强弱判断 + 当不满足特定强弱条件时的保底判断 + """ + # 严格的强分型判断条件 + strong_signals = 0 + + # 判断条件1:成交量显著放大(提高标准) + avg_volume = self._calculate_average_volume(lookback=5) + volume_significantly_amplified = middle_kline.volume > avg_volume * 2.0 if avg_volume > 0 else False + if volume_significantly_amplified: + strong_signals += 1 + + # 判断条件2:中间K线有长下影线(提高标准) + has_long_lower_shadow = middle_info['lower_shadow_ratio'] > 0.6 # 从0.3提高到0.6 + if has_long_lower_shadow: + strong_signals += 1 + + # 判断条件3:后续K线收盘明显偏高(更严格) + middle_range = middle_kline.high - middle_kline.low + last_close_position = (last_kline.close - middle_kline.low) / middle_range if middle_range > 0 else 0.5 + close_significantly_high = last_close_position > 0.7 # 从0.4降低到0.7 + if close_significantly_high: + strong_signals += 1 + + # 判断条件4:最后一根K线是明显的阳线且涨幅较大 + is_significant_bullish = (last_info['is_bullish'] and + last_info['body_size'] > last_info['total_range'] * 0.5) + if is_significant_bullish: + strong_signals += 1 + + # 判断条件5:突破前一根K线重要价位 + breaks_important_level = last_kline.high > first_kline.high + if breaks_important_level: + strong_signals += 1 + + # 需要至少4个强信号才判断为强分型,否则为弱分型 + return 1 if strong_signals >= 4 else -1 \ No newline at end of file diff --git a/__pycache__/ChanBI.cpython-312.pyc b/__pycache__/ChanBI.cpython-312.pyc index b454148..9b13e87 100644 Binary files a/__pycache__/ChanBI.cpython-312.pyc and b/__pycache__/ChanBI.cpython-312.pyc differ diff --git a/__pycache__/ChanBSP.cpython-312.pyc b/__pycache__/ChanBSP.cpython-312.pyc index 177daec..cc05b4c 100644 Binary files a/__pycache__/ChanBSP.cpython-312.pyc and b/__pycache__/ChanBSP.cpython-312.pyc differ diff --git a/__pycache__/ChanCTime.cpython-312.pyc b/__pycache__/ChanCTime.cpython-312.pyc index b9efe3f..c08bfc5 100644 Binary files a/__pycache__/ChanCTime.cpython-312.pyc and b/__pycache__/ChanCTime.cpython-312.pyc differ diff --git a/__pycache__/ChanEnum.cpython-312.pyc b/__pycache__/ChanEnum.cpython-312.pyc index 0e8de31..60e4aad 100644 Binary files a/__pycache__/ChanEnum.cpython-312.pyc and b/__pycache__/ChanEnum.cpython-312.pyc differ diff --git a/__pycache__/ChanKLC.cpython-312.pyc b/__pycache__/ChanKLC.cpython-312.pyc index 7cbe3d4..79c6bf6 100644 Binary files a/__pycache__/ChanKLC.cpython-312.pyc and b/__pycache__/ChanKLC.cpython-312.pyc differ diff --git a/__pycache__/ChanKLU.cpython-312.pyc b/__pycache__/ChanKLU.cpython-312.pyc index 2cfd466..9c66c5f 100644 Binary files a/__pycache__/ChanKLU.cpython-312.pyc and b/__pycache__/ChanKLU.cpython-312.pyc differ diff --git a/__pycache__/ChanLun.cpython-312.pyc b/__pycache__/ChanLun.cpython-312.pyc index 4eb7812..550c702 100644 Binary files a/__pycache__/ChanLun.cpython-312.pyc and b/__pycache__/ChanLun.cpython-312.pyc differ diff --git a/__pycache__/ChanSBI.cpython-312.pyc b/__pycache__/ChanSBI.cpython-312.pyc index 143d523..39238c4 100644 Binary files a/__pycache__/ChanSBI.cpython-312.pyc and b/__pycache__/ChanSBI.cpython-312.pyc differ diff --git a/__pycache__/ChanSEG.cpython-312.pyc b/__pycache__/ChanSEG.cpython-312.pyc index dac2f10..309ff07 100644 Binary files a/__pycache__/ChanSEG.cpython-312.pyc and b/__pycache__/ChanSEG.cpython-312.pyc differ diff --git a/__pycache__/ChanZS.cpython-312.pyc b/__pycache__/ChanZS.cpython-312.pyc index c3d3626..db18ccb 100644 Binary files a/__pycache__/ChanZS.cpython-312.pyc and b/__pycache__/ChanZS.cpython-312.pyc differ diff --git a/web/app.py b/web/app.py index 70e91a4..d03c761 100644 --- a/web/app.py +++ b/web/app.py @@ -221,10 +221,12 @@ def analyze_chan(df): for klc in klc_list: if hasattr(klc, 'klc_fx_type') and klc.klc_fx_type != Chan_KLC_FX.UNKNOWN: # 计算分型强度 - fx_strength = klc.calculate_fx_strength() + #fx_strength = klc.calculate_fx_strength() + fx_strength = klc.cal_fx_strength() fx_strength_level = klc.get_fx_strength_level() is_strong_fx = klc.is_strong_fx() - + if fx_strength < 1: + fx_strength = 0 klc_fx_info.append({ 'time': klc.end_time, 'price': klc.low if klc.fx == Chan_FX_TYPE.BOTTOM else klc.high, diff --git a/web/templates/index.html b/web/templates/index.html index 85b422a..834f42a 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -709,7 +709,7 @@ // 添加买卖点悬浮提示元素 const tooltipElement = document.createElement('div'); tooltipElement.className = 'point-tooltip'; - document.body.appendChild(tooltipElement); + // document.body.appendChild(tooltipElement); // 助手函数:转换UTC时间到所选时区 function convertToTimezone(utcDate, timezone) { @@ -2731,8 +2731,10 @@ fx_strength_level: fx.fx_strength_level, is_strong_fx: fx.is_strong_fx }); - const displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`; + if (fx.fx_strength < 1) { + displayText = '' + } console.log('显示文本:', displayText); // 添加标记配置调试 @@ -2821,6 +2823,9 @@ } // 构建小周期分型显示文本 + if (fx.fx_strength < 1){ + displayText = '' + } const displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`; // 小周期分型标记配置 - 根据分型类型使用正确的箭头形状 @@ -2828,7 +2833,6 @@ time: timestamp, position: fx.is_bottom ? 'belowBar' : 'aboveBar', color: strengthColor, - shape: fx.is_bottom ? 'arrowUp' : 'arrowDown', // 底分型向上箭头,顶分型向下箭头 text: displayText, size: fx.is_strong_fx ? 2 : 1 }; @@ -2917,9 +2921,11 @@ // 绑定同步事件 bindSyncEvents(mainChartContainer, volumeChartContainer, macdChartContainer, mainChart, volumeChart, macdChart, showMacd); - // 设置图表默认时间范围 - setDefaultTimeRange(); - + // 只有在时间输入框都为空时才设置图表默认时间范围 + if (!$('#start_time').val() && !$('#end_time').val()) { + setDefaultTimeRange(); + } + // 添加买卖点提示 setupTooltip(mainChart); @@ -3193,7 +3199,7 @@ // 添加买卖点悬浮提示元素 const tooltipElement = document.createElement('div'); tooltipElement.className = 'point-tooltip'; - document.body.appendChild(tooltipElement); + // document.body.appendChild(tooltipElement); // 添加自定义十字线信息显示 const crosshairTooltip = document.createElement('div'); @@ -3207,7 +3213,7 @@ crosshairTooltip.style.zIndex = '1000'; crosshairTooltip.style.pointerEvents = 'none'; crosshairTooltip.style.display = 'none'; - document.body.appendChild(crosshairTooltip); + // document.body.appendChild(crosshairTooltip); // 添加鼠标悬停事件显示提示 if (mainChart) {