diff --git a/ChanKLC.py b/ChanKLC.py index c613b9d..d7ab700 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -160,7 +160,7 @@ class ChanKLC(): klu_list.append(klc3.klus) gap = klc3.end_klu.index - klc1.start_klu.index + 1 if gap < 4: - print(klc1.start_time, klc1.start_klu.index, klc3.end_time, klc3.end_klu.index, gap, self.cal_fx_strength(), self.klc_fx_type) + pass return gap def get_feature_data(self): features = dict() @@ -1214,7 +1214,7 @@ class ChanKLC(): # 分型质量调整 base_score += fx_quality - print(self.start_time, base_score, is_bi_end, post_fx_confirmation, fx_quality) + #print(self.start_time, base_score, is_bi_end, post_fx_confirmation, fx_quality) # 2025-06-07 08:15:00 1.5 0 0.8 0.7 # 限制在-3到3范围内 return max(-3, min(3, base_score)) @@ -1245,7 +1245,7 @@ class ChanKLC(): if len(subsequent_klcs) < 2: return 0 - print(subsequent_klcs[len(subsequent_klcs)-1].start_time) + pass if self.fx == Chan_FX_TYPE.TOP: return self._check_top_bi_ending(subsequent_klcs) else: # BOTTOM diff --git a/web/app.py b/web/app.py index 2513ebf..9691a71 100644 --- a/web/app.py +++ b/web/app.py @@ -80,7 +80,6 @@ def get_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None): elif symbol_type == 'a_stock': return get_a_stock_kl_data(symbol, timeframe, limit, start_time, end_time) else: - print(f"未知的交易对类型: {symbol}") return None def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None): @@ -92,7 +91,7 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time= try: since = int(start_time) except ValueError: - print(f"无效的起始时间: {start_time}") + pass # 结束时间处理 until = None @@ -100,7 +99,7 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time= try: until = int(end_time) except ValueError: - print(f"无效的结束时间: {end_time}") + pass # 根据时间周期调整每次请求的数据量 batch_size = 1000 # 默认批次大小 @@ -121,45 +120,36 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time= request_count = 0 max_requests = 50 # 最大请求次数,防止无限循环 - print(f"开始分批获取加密货币数据: {symbol}, {timeframe}") - # 分页加载数据 while request_count < max_requests: request_count += 1 - print(f"批次 {request_count}: 获取数据 since={current_since}, limit={batch_size}") - try: # 获取当前页的数据 ohlcv = exchange.fetch_ohlcv(symbol, timeframe, since=current_since, limit=batch_size) # 如果没有获取到数据,结束循环 if not ohlcv or len(ohlcv) == 0: - print(f"批次 {request_count}: 未获取到数据,结束") break # 将获取到的数据添加到总列表中 all_ohlcv.extend(ohlcv) - print(f"批次 {request_count}: 获取到 {len(ohlcv)} 条记录") # 获取最后一条数据的时间戳 last_timestamp = ohlcv[-1][0] # 如果已达到结束时间,结束循环 if until and last_timestamp >= until: - print(f"批次 {request_count}: 已达到结束时间,结束") break # 如果获取的数据条数小于限制数,说明已经获取完所有数据 if len(ohlcv) < batch_size: - print(f"批次 {request_count}: 数据不足批次大小,已获取完所有数据") break # 更新下一页的开始时间(加1毫秒避免重复) current_since = last_timestamp + 1 except Exception as e: - print(f"批次 {request_count} 获取失败: {e}") # 如果单个批次失败,继续尝试下一个批次 if current_since: # 尝试增加时间跳过可能的问题时间点 @@ -172,7 +162,6 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time= # 数据为空的情况 if not all_ohlcv or len(all_ohlcv) == 0: - print(f"未获取到数据: {symbol}, {timeframe}") return None # 转换为DataFrame @@ -192,26 +181,18 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time= # 限制数据条数的逻辑 - 优先考虑时间范围 if start_time and end_time: # 如果指定了明确的时间范围,返回该时间范围内的所有数据 - print(f"用户指定了时间范围,返回完整数据 {len(df)} 条记录") if len(df) > 10000: # 防止数据量过大,设置一个合理的上限 - print(f"警告:数据量过大({len(df)}条),为保证性能将限制为最新的10000条记录") df = df.tail(10000).reset_index(drop=True) elif limit and len(df) > limit: # 如果没有指定明确时间范围,使用默认的limit限制 - print(f"未指定明确时间范围,应用默认限制,返回最新的 {limit} 条记录") df = df.tail(limit).reset_index(drop=True) # 如果过滤后没有数据,返回None if len(df) == 0: - print("过滤后无数据") return None - - print(f"成功获取加密货币数据: {len(df)} 条记录 (共 {request_count} 个批次)") return df except Exception as e: - print(f"获取加密货币数据错误: {e}") - traceback.print_exc() return None def get_a_stock_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None): @@ -256,22 +237,16 @@ def get_a_stock_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time # 如果用户指定了时间范围,优先获取该范围内的所有数据 actual_limit = limit if start_date and end_date: - print(f"用户指定了时间范围 {start_date} 到 {end_date},将获取该范围内的所有数据") actual_limit = None # 不限制数据条数,获取完整时间范围数据 # 调用A股数据获取器 df = china_stock.get_kl_data(symbol, timeframe, start_date, end_date, actual_limit) if df is None: - print(f"未获取到A股数据: {symbol}") return None - - print(f"获取到A股数据: {len(df)} 条记录") return df except Exception as e: - print(f"获取A股数据错误: {e}") - traceback.print_exc() return None def add_indicators(df): @@ -353,13 +328,152 @@ def analyze_chan(df): for bi in bi_list: bi.cal_macd_div() #print(bi.start_time, bi.macd_hist, bi.macd_div) - -def generate_replay_data(df, client_tz): - """生成逐步计算的回放数据""" - print(f"开始生成回放数据,K线总数: {len(df)}") + # 获取原始K线数据用于KLU分型分析 + klu_list = [] + try: + # 尝试获取KLU数据 + if hasattr(chan, 'get_klu_list'): + klu_list = chan.get_klu_list(df) + elif hasattr(chan, 'klu_list'): + klu_list = chan.klu_list + else: + # 如果没有专门的KLU方法,尝试从KLC获取原始K线数据 + pass + except Exception as e: + klu_list = [] + + # 提取K线分型信息 + klc_fx_info = [] + for klc in klc_list: + if hasattr(klc, 'klc_fx_type') and klc.klc_fx_type != Chan_KLC_FX.UNKNOWN: + try: + # 计算分型强度 + fx_strength = 0 + fx_strength_level = "" + is_strong_fx = False + + # 统一使用cal_fx_strength函数 + if hasattr(klc, 'cal_fx_strength'): + fx_strength = klc.cal_fx_strength() + + # 尝试获取分型强度等级 + if hasattr(klc, 'get_fx_strength_level'): + fx_strength_level = klc.get_fx_strength_level() + + # 尝试判断是否为强分型 + if hasattr(klc, 'is_strong_fx'): + is_strong_fx = klc.is_strong_fx() + + # 如果分型强度小于1,设为0 + 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, + 'fx_type': str(klc.klc_fx_type).replace("Chan_KLC_FX.", ""), + 'is_bottom': klc.fx == Chan_FX_TYPE.BOTTOM, + 'fx_strength': fx_strength, # 分型强度分数 (0-100) + 'fx_strength_level': fx_strength_level, # 分型强度等级 (极强/强/中等/弱/极弱) + 'is_strong_fx': is_strong_fx # 是否为强分型 + }) + except Exception as e: + # 如果出错,仍然添加基本信息,但分型强度为0 + klc_fx_info.append({ + 'time': klc.end_time, + 'price': klc.low if klc.fx == Chan_FX_TYPE.BOTTOM else klc.high, + 'fx_type': str(klc.klc_fx_type).replace("Chan_KLC_FX.", ""), + 'is_bottom': klc.fx == Chan_FX_TYPE.BOTTOM, + 'fx_strength': 0, + 'fx_strength_level': "", + 'is_strong_fx': False + }) + + # 提取KLU分型信息 + klu_fx_info = [] + for klu in klu_list: + if hasattr(klu, 'fx_type') and klu.fx_type != Chan_FX_TYPE.UNKNOWN: + try: + # 计算分型强度 + fx_strength = 0 + fx_strength_level = "" + is_strong_fx = False + + # 尝试调用分型强度计算方法 + if hasattr(klu, 'calculate_realtime_fx_strength'): + fx_strength = klu.calculate_realtime_fx_strength() + elif hasattr(klu, 'fx_strength'): + fx_strength = klu.fx_strength + + # 尝试获取分型强度等级 - 基于强度值生成等级 + if fx_strength >= 2: + fx_strength_level = "强" + is_strong_fx = True + elif fx_strength >= 1: + fx_strength_level = "中" + is_strong_fx = False + elif fx_strength >= 0: + fx_strength_level = "弱" + is_strong_fx = False + else: + fx_strength_level = "极弱" + is_strong_fx = False + + # 确保分型确认状态 + is_confirmed = getattr(klu, 'fx_confirmed', True) + + klu_fx_info.append({ + 'time': klu.time, + 'price': klu.low if klu.fx_type == Chan_FX_TYPE.BOTTOM else klu.high, + 'fx_type': str(klu.fx_type).replace("Chan_FX_TYPE.", ""), + 'is_bottom': klu.fx_type == Chan_FX_TYPE.BOTTOM, + 'fx_strength': fx_strength, # 分型强度分数 + 'fx_strength_level': fx_strength_level, # 分型强度等级 + 'is_strong_fx': is_strong_fx, # 是否为强分型 + 'fx_confirmed': is_confirmed # 分型是否确认 + }) + except Exception as e: + # 如果出错,仍然添加基本信息,但分型强度为0 + klu_fx_info.append({ + 'time': klu.time, + 'price': klu.low if klu.fx_type == Chan_FX_TYPE.BOTTOM else klu.high, + 'fx_type': str(klu.fx_type).replace("Chan_FX_TYPE.", ""), + 'is_bottom': klu.fx_type == Chan_FX_TYPE.BOTTOM, + 'fx_strength': 0, + 'fx_strength_level': "", + 'is_strong_fx': False, + 'fx_confirmed': False + }) + + + + return { + 'klc_list': klc_list, + 'klu_list': klu_list, # 添加KLU列表 + 'bi_list': bi_list, + 'seg_list': seg_list, + 'zs_list': zs_list, + 'trade_points': buy_sell_points, + 'klc_fx_info': klc_fx_info, # KLC分型信息 + 'klu_fx_info': klu_fx_info # 添加KLU分型信息 + } + +def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, start_time=None, end_time=None): + """生成逐步计算的回放数据""" replay_data = {} + # 预先获取和分析完整的次周期数据(避免重复计算) + element_full_data = None + element_analysis_full = None + if element_timeframe and symbol: + # 一次性获取完整的次周期数据 + element_full_data = get_kl_data(symbol, element_timeframe, start_time=start_time, end_time=end_time) + if element_full_data is not None and len(element_full_data) > 0: + # 一次性添加技术指标和进行缠论分析 + element_full_data = add_indicators(element_full_data) + element_analysis_full = analyze_chan(element_full_data) + # 为每个K线索引计算分析结果 for i in range(1, len(df) + 1): # 从1开始,至少需要1根K线 try: @@ -375,6 +489,161 @@ def generate_replay_data(df, client_tz): # 计算MACD macd_data = calculate_macd(current_df) + # 如果有次周期数据,筛选对应时间范围的数据 + element_step_data = {} + if element_full_data is not None and element_analysis_full is not None: + # 获取当前主周期时间范围 + current_end_time = current_df['timestamp'].iloc[-1] if len(current_df) > 0 else None + + if current_end_time: + # 筛选次周期数据:只取时间戳小于等于当前主周期结束时间的数据 + element_current_df = element_full_data[element_full_data['timestamp'] <= current_end_time].copy() + + if len(element_current_df) > 0: + # 筛选对应的分析结果 + def filter_by_time(items, time_attr='end_time'): + """根据时间筛选分析结果""" + filtered = [] + for item in items: + try: + if hasattr(item, time_attr): + item_time = getattr(item, time_attr) + if item_time: + if isinstance(item_time, str): + item_timestamp = pd.to_datetime(item_time).timestamp() * 1000 + else: + item_timestamp = item_time.timestamp() * 1000 + + if item_timestamp <= current_end_time: + filtered.append(item) + elif hasattr(item, 'end_klc') and item.end_klc: + item_time = item.end_klc.end_time + if item_time: + if isinstance(item_time, str): + item_timestamp = pd.to_datetime(item_time).timestamp() * 1000 + else: + item_timestamp = item_time.timestamp() * 1000 + + if item_timestamp <= current_end_time: + filtered.append(item) + except: + continue + return filtered + + # 筛选笔、线段、中枢数据 + filtered_bi_list = filter_by_time(element_analysis_full['bi_list']) + filtered_seg_list = filter_by_time(element_analysis_full['seg_list']) + filtered_zs_list = filter_by_time(element_analysis_full['zs_list']) + + # 筛选买卖点(基于字典格式) + filtered_trade_points = [] + for point in element_analysis_full['trade_points']: + try: + point_time = point['time'] + if isinstance(point_time, str): + point_timestamp = pd.to_datetime(point_time).timestamp() * 1000 + else: + point_timestamp = point_time.timestamp() * 1000 + + if point_timestamp <= current_end_time: + filtered_trade_points.append(point) + except: + continue + + # 筛选分型信息 + def filter_fx_info(fx_list): + filtered = [] + for fx in fx_list: + try: + fx_time = fx['time'] + if isinstance(fx_time, str): + fx_timestamp = pd.to_datetime(fx_time).timestamp() * 1000 + else: + fx_timestamp = fx_time.timestamp() * 1000 + + if fx_timestamp <= current_end_time: + filtered.append(fx) + except: + continue + return filtered + + filtered_klc_fx = filter_fx_info(element_analysis_full['klc_fx_info']) + filtered_klu_fx = filter_fx_info(element_analysis_full['klu_fx_info']) + + # 计算当前时间范围的MACD + element_macd_data = calculate_macd(element_current_df) + + element_step_data = { + 'element_kline_data': clean_dataframe_for_json(element_current_df).to_dict('records'), + '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), + 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 + } for bi in filtered_bi_list if bi.end_klc], + '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) + } for seg in filtered_seg_list if seg.end_bi], + 'element_zs_list': [{ + 'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(), + 'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None, + 'zg': zs.zg, + 'zd': zs.zd, + 'is_sure': zs.is_sure + } for zs in filtered_zs_list if zs.end_klc], + 'element_uncompleted_zs_list': [{ + 'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(), + 'end_time': None, + 'zg': zs.zg, + 'zd': zs.zd, + 'is_sure': zs.is_sure + } for zs in filtered_zs_list if not zs.is_sure], + 'element_trade_points': [{ + 'type': point['type'], + 'time': format_time_safely(point['time'], client_tz), + 'price': point['price'], + 'desc': point['desc'] + } for point in filtered_trade_points], + 'element_macd': element_macd_data, + 'element_bollinger': { + 'upper': element_current_df['bb_upper'].tolist(), + 'middle': element_current_df['bb_middle'].tolist(), + 'lower': element_current_df['bb_lower'].tolist() + }, + 'element_element_bollinger': { + 'upper': element_current_df['element_bb_upper'].tolist(), + 'middle': element_current_df['element_bb_middle'].tolist(), + 'lower': element_current_df['element_bb_lower'].tolist() + }, + 'element_klc_fx_info': [{ + 'time': format_time_safely(point['time'], client_tz), + 'price': float(point['price']), + 'fx_type': point['fx_type'], + 'is_bottom': bool(point['is_bottom']), + 'fx_strength': float(point['fx_strength']), + 'fx_strength_level': str(point['fx_strength_level']), + 'is_strong_fx': bool(point['is_strong_fx']) + } for point in filtered_klc_fx], + 'element_klu_fx_info': [{ + 'time': format_time_safely(point['time'], client_tz), + 'price': float(point['price']), + 'fx_type': point['fx_type'], + 'is_bottom': bool(point['is_bottom']), + 'fx_strength': float(point['fx_strength']), + 'fx_strength_level': str(point['fx_strength_level']), + 'is_strong_fx': bool(point['is_strong_fx']), + 'fx_confirmed': bool(point['fx_confirmed']) + } for point in filtered_klu_fx] + } + # 构建该索引对应的分析结果 step_data = { 'kline_data': clean_dataframe_for_json(current_df).to_dict('records'), @@ -447,158 +716,21 @@ def generate_replay_data(df, client_tz): } for point in analysis_result['klu_fx_info']] } + # 合并次周期数据到step_data中 + step_data.update(element_step_data) + replay_data[i-1] = step_data # 使用0-based索引 - # 每处理100个点输出一次进度 - if i % 100 == 0 or i == len(df): - print(f"生成回放数据进度: {i}/{len(df)}") except Exception as e: - print(f"生成第{i}步回放数据时出错: {e}") continue - - print(f"回放数据生成完成,总步数: {len(replay_data)}") return replay_data - - # 获取原始K线数据用于KLU分型分析 - klu_list = [] - try: - # 尝试获取KLU数据 - if hasattr(chan, 'get_klu_list'): - klu_list = chan.get_klu_list(df) - elif hasattr(chan, 'klu_list'): - klu_list = chan.klu_list - else: - # 如果没有专门的KLU方法,尝试从KLC获取原始K线数据 - print("未找到KLU数据获取方法,尝试其他方式") - except Exception as e: - print(f"获取KLU数据时出错: {e}") - klu_list = [] - - # 提取K线分型信息 - klc_fx_info = [] - for klc in klc_list: - if hasattr(klc, 'klc_fx_type') and klc.klc_fx_type != Chan_KLC_FX.UNKNOWN: - try: - # 计算分型强度 - fx_strength = 0 - fx_strength_level = "" - is_strong_fx = False - - # 统一使用cal_fx_strength函数 - if hasattr(klc, 'cal_fx_strength'): - fx_strength = klc.cal_fx_strength() - - # 尝试获取分型强度等级 - if hasattr(klc, 'get_fx_strength_level'): - fx_strength_level = klc.get_fx_strength_level() - - # 尝试判断是否为强分型 - if hasattr(klc, 'is_strong_fx'): - is_strong_fx = klc.is_strong_fx() - - # 如果分型强度小于1,设为0 - 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, - 'fx_type': str(klc.klc_fx_type).replace("Chan_KLC_FX.", ""), - 'is_bottom': klc.fx == Chan_FX_TYPE.BOTTOM, - 'fx_strength': fx_strength, # 分型强度分数 (0-100) - 'fx_strength_level': fx_strength_level, # 分型强度等级 (极强/强/中等/弱/极弱) - 'is_strong_fx': is_strong_fx # 是否为强分型 - }) - except Exception as e: - print(f"处理KLC分型信息时出错: {e}") - # 如果出错,仍然添加基本信息,但分型强度为0 - klc_fx_info.append({ - 'time': klc.end_time, - 'price': klc.low if klc.fx == Chan_FX_TYPE.BOTTOM else klc.high, - 'fx_type': str(klc.klc_fx_type).replace("Chan_KLC_FX.", ""), - 'is_bottom': klc.fx == Chan_FX_TYPE.BOTTOM, - 'fx_strength': 0, - 'fx_strength_level': "", - 'is_strong_fx': False - }) - - # 提取KLU分型信息 - klu_fx_info = [] - for klu in klu_list: - if hasattr(klu, 'fx_type') and klu.fx_type != Chan_FX_TYPE.UNKNOWN: - try: - # 计算分型强度 - fx_strength = 0 - fx_strength_level = "" - is_strong_fx = False - - # 尝试调用分型强度计算方法 - if hasattr(klu, 'calculate_realtime_fx_strength'): - fx_strength = klu.calculate_realtime_fx_strength() - elif hasattr(klu, 'fx_strength'): - fx_strength = klu.fx_strength - - # 尝试获取分型强度等级 - 基于强度值生成等级 - if fx_strength >= 2: - fx_strength_level = "强" - is_strong_fx = True - elif fx_strength >= 1: - fx_strength_level = "中" - is_strong_fx = False - elif fx_strength >= 0: - fx_strength_level = "弱" - is_strong_fx = False - else: - fx_strength_level = "极弱" - is_strong_fx = False - - # 确保分型确认状态 - is_confirmed = getattr(klu, 'fx_confirmed', True) - - klu_fx_info.append({ - 'time': klu.time, - 'price': klu.low if klu.fx_type == Chan_FX_TYPE.BOTTOM else klu.high, - 'fx_type': str(klu.fx_type).replace("Chan_FX_TYPE.", ""), - 'is_bottom': klu.fx_type == Chan_FX_TYPE.BOTTOM, - 'fx_strength': fx_strength, # 分型强度分数 - 'fx_strength_level': fx_strength_level, # 分型强度等级 - 'is_strong_fx': is_strong_fx, # 是否为强分型 - 'fx_confirmed': is_confirmed # 分型是否确认 - }) - except Exception as e: - print(f"处理KLU分型信息时出错: {e}") - # 如果出错,仍然添加基本信息,但分型强度为0 - klu_fx_info.append({ - 'time': klu.time, - 'price': klu.low if klu.fx_type == Chan_FX_TYPE.BOTTOM else klu.high, - 'fx_type': str(klu.fx_type).replace("Chan_FX_TYPE.", ""), - 'is_bottom': klu.fx_type == Chan_FX_TYPE.BOTTOM, - 'fx_strength': 0, - 'fx_strength_level': "", - 'is_strong_fx': False, - 'fx_confirmed': False - }) - - print(f"提取到 {len(klc_fx_info)} 个KLC分型和 {len(klu_fx_info)} 个KLU分型") - - return { - 'klc_list': klc_list, - 'klu_list': klu_list, # 添加KLU列表 - 'bi_list': bi_list, - 'seg_list': seg_list, - 'zs_list': zs_list, - 'trade_points': buy_sell_points, - 'klc_fx_info': klc_fx_info, # KLC分型信息 - 'klu_fx_info': klu_fx_info # 添加KLU分型信息 - } def identify_trade_points(bi_list, seg_list, zs_list): """识别缠论买卖点 - 多级别识别,减少滞后性""" trade_points = [] - # 输出调试信息 - print(f"识别买卖点:总共 {len(bi_list)} 个笔, {len(seg_list)} 个线段, {len(zs_list)} 个中枢") + # 1. 基于笔的二三类买卖点识别(更及时) trade_points.extend(identify_bi_trade_points(bi_list, zs_list)) @@ -615,7 +747,6 @@ def identify_trade_points(bi_list, seg_list, zs_list): # 按时间排序 trade_points.sort(key=lambda x: x['time']) - print(f"总共识别出 {len(trade_points)} 个买卖点") return trade_points def identify_bi_trade_points(bi_list, zs_list): @@ -911,7 +1042,6 @@ def analyze(): # 验证交易对不为空 if not symbol or symbol.strip() == '': - print(f"错误: 空交易对") return jsonify({'error': '交易对不能为空'}) # 获取时间范围参数 @@ -931,24 +1061,16 @@ def analyze(): # 获取是否需要回放数据的参数 need_replay_data = request.args.get('need_replay_data', 'false').lower() == 'true' - print(f"API请求参数: symbol={symbol}, timeframe={timeframe}, element_timeframe={element_timeframe}") - print(f"时间范围: start_time={start_time}, end_time={end_time}") - print(f"elements_only参数: 原始值={elements_only_param}, 处理后={elements_only}") - print(f"need_replay_data参数: {need_replay_data}") - # 验证小周期是否小于主周期 if element_timeframe and not is_smaller_or_equal_timeframe(element_timeframe, timeframe): - print(f"错误: 元素周期 {element_timeframe} 大于主周期 {timeframe}") return jsonify({'error': '分形元素时间周期必须小于或等于主图表时间周期'}) # 获取数据 df = get_kl_data(symbol, timeframe, start_time=start_time, end_time=end_time) if df is None: - print(f"错误: 获取数据失败 - symbol={symbol}, timeframe={timeframe}") return jsonify({'error': '获取数据失败'}) if len(df) == 0: - print(f"错误: 所选时间范围内没有数据 - symbol={symbol}, timeframe={timeframe}") return jsonify({'error': '所选时间范围内没有数据'}) # 使用客户端指定的时区 @@ -961,8 +1083,6 @@ def analyze(): # 如果不是只需要分形元素数据,则添加主周期数据 if not elements_only: - print(f"处理主周期数据 (elements_only={elements_only})") - # 添加技术指标(包括布林带) df = add_indicators(df) @@ -974,9 +1094,7 @@ def analyze(): # 如果需要回放数据,生成逐步计算的回放数据 if need_replay_data: - print("开始生成回放数据...") - replay_data = generate_replay_data(df, client_tz) - print(f"回放数据生成完成,包含 {len(replay_data)} 个步骤") + replay_data = generate_replay_data(df, client_tz, symbol, element_timeframe, start_time, end_time) else: replay_data = None @@ -1058,12 +1176,9 @@ def analyze(): # 如果生成了回放数据,添加到返回结果中 if replay_data is not None: result['replay_data'] = replay_data - else: - print(f"只请求元素数据,跳过主周期数据处理 (elements_only={elements_only})") # 如果有指定分形元素时间周期,获取小周期数据 if element_timeframe: - print(f"处理元素周期数据: {element_timeframe}") # 获取小周期数据,使用与主周期相同的时间范围 element_df = get_kl_data(symbol, element_timeframe, start_time=start_time, end_time=end_time) @@ -1160,9 +1275,7 @@ def analyze(): 'fx_confirmed': bool(point['fx_confirmed']) # 分型是否确认 } for point in element_analysis['klu_fx_info']] - print(f"小周期分析完成: {element_timeframe}, 笔数量: {len(result['element_bi_list'])}, {'仅元素数据' if elements_only else '包含主周期数据'}") - else: - print(f"无法获取小周期数据: {element_timeframe}") + pass return jsonify(result) @@ -1248,23 +1361,17 @@ def filter_stocks(): stock_list = [] data_source = "" try: - print("正在获取完整股票列表...") stock_list = china_stock.get_stock_list() if stock_list and len(stock_list) > 0: - print(f"成功获取完整股票列表: {len(stock_list)} 只股票") data_source = "完整股票列表" else: raise Exception("获取到的股票列表为空") except Exception as e: - print(f"获取完整股票列表失败: {e}") - print("使用热门股票列表作为备用...") try: popular_stocks = china_stock.get_popular_stocks() stock_list = [{'symbol': stock['symbol'], 'name': stock['name']} for stock in popular_stocks] - print(f"使用热门股票列表: {len(stock_list)} 只股票") data_source = "热门股票列表" except Exception as e2: - print(f"获取热门股票列表也失败: {e2}") # 检查是否是网络连接问题 if "timeout" in str(e).lower() or "connection" in str(e).lower() or "network" in str(e).lower(): return jsonify({ @@ -1287,18 +1394,12 @@ def filter_stocks(): total_count = len(stock_list) failed_count = 0 - print(f"开始筛选股票,总数: {total_count}, 时间范围: {start_time} 到 {end_time}, 周期: {timeframe}") - for stock in stock_list: try: symbol = stock['symbol'] name = stock['name'] processed_count += 1 - # 每处理20只股票打印一次进度 - if processed_count % 20 == 0: - print(f"已处理 {processed_count}/{total_count} 只股票,成功: {len(results)}, 失败: {failed_count}") - # 获取股票K线数据 df = get_a_stock_kl_data(symbol, timeframe, start_time=start_time, end_time=end_time) @@ -1306,7 +1407,6 @@ def filter_stocks(): failed_count += 1 # 如果连续失败太多,可能是网络问题 if failed_count > 10 and len(results) == 0: - print(f"连续失败 {failed_count} 次,可能是网络问题") return jsonify({ 'error': '网络连接不稳定,无法获取股票数据。请检查网络连接后重试。', 'error_type': 'network_error', @@ -1357,12 +1457,9 @@ def filter_stocks(): break # 找到一个满足条件的就跳出循环 except Exception as e: - print(f"处理股票 {symbol} 时出错: {str(e)}") failed_count += 1 continue - print(f"筛选完成,共找到 {len(results)} 只满足条件的股票") - # 按分型强度降序排列 results.sort(key=lambda x: x['fx_strength'], reverse=True) @@ -1376,7 +1473,6 @@ def filter_stocks(): }) except Exception as e: - print(f"筛选股票时发生错误: {str(e)}") # 检查是否是网络连接问题 if "timeout" in str(e).lower() or "connection" in str(e).lower() or "network" in str(e).lower(): return jsonify({ diff --git a/web/cn_stock_data.py b/web/cn_stock_data.py index cb916f9..42280cd 100644 --- a/web/cn_stock_data.py +++ b/web/cn_stock_data.py @@ -25,7 +25,7 @@ class ChinaStockData: # 设置较短的超时时间,避免长时间等待 import akshare as ak - print("正在获取A股股票列表...") + pass # 尝试获取沪深A股实时行情,设置超时时间 try: @@ -42,12 +42,11 @@ class ChinaStockData: delattr(requests, 'timeout') except Exception as network_error: - print(f"网络请求失败: {network_error}") + pass # 网络失败时返回空列表,让调用方使用备用方案 return [] if stock_info is None or len(stock_info) == 0: - print("获取到的股票数据为空") return [] # 增加到前2000只股票,提供更多选择 @@ -66,16 +65,13 @@ class ChinaStockData: 'amount': float(row['成交额']) if pd.notna(row['成交额']) else 0.0 }) except Exception as row_error: - print(f"处理股票数据行时出错: {row_error}") continue # 按成交金额排序,优先显示活跃股票 stock_list.sort(key=lambda x: x['amount'], reverse=True) - print(f"成功获取 {len(stock_list)} 只股票") return stock_list except Exception as e: - print(f"获取股票列表失败: {e}") return [] def get_popular_stocks(self): @@ -224,7 +220,7 @@ class ChinaStockData: if '-' in end_date: end_date = end_date.replace('-', '') - print(f"获取A股数据: {symbol}, 周期: {timeframe}, 开始: {start_date}, 结束: {end_date}") + pass # 分批次获取数据以突破单次限制 all_data = [] @@ -252,7 +248,7 @@ class ChinaStockData: current_end_dt = current_start_dt + timedelta(days=batch_days) current_end = min(current_end_dt.strftime('%Y%m%d'), end_date) - print(f"批次 {iteration_count}: 获取 {current_start} 到 {current_end} 的数据") + pass try: # 根据时间周期选择不同的API @@ -294,13 +290,11 @@ class ChinaStockData: df_batch = self.adjust_timestamp_for_trading_hours(df_batch, timeframe) all_data.append(df_batch) - print(f"批次 {iteration_count}: 获取到 {len(df_batch)} 条记录") - else: - print(f"批次 {iteration_count}: 未获取到数据") + pass except Exception as e: - print(f"批次 {iteration_count} 获取失败: {e}") # 继续下一个批次 + pass # 更新下一批次的开始时间 current_start = (current_end_dt + timedelta(days=1)).strftime('%Y%m%d') @@ -310,7 +304,6 @@ class ChinaStockData: # 合并所有批次的数据 if not all_data: - print(f"未获取到任何数据: {symbol}") return None # 合并DataFrame @@ -330,17 +323,13 @@ class ChinaStockData: # 检查是否指定了明确的时间范围 if start_date and end_date: # 如果指定了时间范围,优先返回完整的时间范围数据 - print(f"用户指定了时间范围 {start_date} 到 {end_date},返回完整数据 {len(df)} 条记录") if len(df) > 10000: # 防止数据量过大,设置一个合理的上限 - print(f"警告:数据量过大({len(df)}条),为保证性能将限制为最新的10000条记录") df = df.tail(10000).reset_index(drop=True) else: # 如果没有指定时间范围,使用默认的limit限制 - print(f"未指定明确时间范围,应用默认限制,返回最新的 {limit} 条记录") df = df.tail(limit).reset_index(drop=True) elif limit is None and len(df) > 10000: # 即使没有limit限制,也要防止数据量过大影响性能 - print(f"无limit限制但数据量过大({len(df)}条),为保证性能将限制为最新的10000条记录") df = df.tail(10000).reset_index(drop=True) # 添加技术指标 @@ -351,7 +340,6 @@ class ChinaStockData: # 检查并处理任何剩余的NaN值 if df.isnull().any().any(): - print("警告:发现NaN值,正在清理...") # 对于数值列,用0填充NaN numeric_cols = df.select_dtypes(include=[np.number]).columns for col in numeric_cols: @@ -367,12 +355,9 @@ class ChinaStockData: for col in df.select_dtypes(include=[np.number]).columns: df[col] = df[col].replace([np.inf, -np.inf], 0 if col != 'volume_ratio' else 1.0) - print(f"成功获取A股数据: {len(df)} 条记录 (共 {len(all_data)} 个批次)") return df except Exception as e: - print(f"获取A股数据失败: {e}") - traceback.print_exc() return None def add_indicators(self, df): @@ -417,7 +402,6 @@ class ChinaStockData: return df except Exception as e: - print(f"添加指标失败: {e}") return df def search_stock(self, keyword): @@ -478,7 +462,7 @@ class ChinaStockData: break except Exception as e: - print(f"全市场搜索失败: {e}") + pass # 排序:优先显示代码匹配的结果 def sort_key(item): @@ -495,7 +479,6 @@ class ChinaStockData: return results[:20] except Exception as e: - print(f"搜索股票失败: {e}") return [] def get_stock_by_sector(self, sector=None): @@ -514,7 +497,6 @@ class ChinaStockData: sectors[sector_name].append(stock) return sectors except Exception as e: - print(f"获取行业股票失败: {e}") return {} if sector is None else [] def get_all_sectors(self): @@ -527,7 +509,6 @@ class ChinaStockData: sectors.add(sector) return sorted(list(sectors)) except Exception as e: - print(f"获取行业分类失败: {e}") return [] def is_trading_day(self, date): @@ -547,7 +528,6 @@ class ChinaStockData: # 目前暂时只过滤周末 return True except Exception as e: - print(f"判断交易日失败: {e}") return True # 默认返回True,避免过度过滤 def is_trading_time(self, dt): @@ -569,7 +549,6 @@ class ChinaStockData: return ((morning_start <= time_str <= morning_end) or (afternoon_start <= time_str <= afternoon_end)) except Exception as e: - print(f"判断交易时间失败: {e}") return True # 默认返回True,避免过度过滤 def adjust_timestamp_for_trading_hours(self, df, timeframe): @@ -605,8 +584,6 @@ class ChinaStockData: return df.reset_index(drop=True) except Exception as e: - print(f"调整A股时间戳失败: {e}") - traceback.print_exc() return df def get_trading_calendar(self, start_date, end_date): @@ -627,7 +604,6 @@ class ChinaStockData: return trading_days except Exception as e: - print(f"获取交易日历失败: {e}") # 如果获取失败,生成简单的工作日列表(排除周末) trading_days = [] current = pd.to_datetime(start_date) @@ -721,8 +697,6 @@ class ChinaStockData: return df except Exception as e: - print(f"填补交易时间间隙失败: {e}") - traceback.print_exc() return df def clean_a_stock_data(self, df, timeframe): @@ -778,5 +752,4 @@ class ChinaStockData: return df.reset_index(drop=True) except Exception as e: - print(f"清理A股数据失败: {e}") return df \ No newline at end of file