修改更新 的一些小bug

This commit is contained in:
2026-01-22 16:18:38 +08:00
parent 017bdf2b47
commit 8c9fef0383
6 changed files with 933 additions and 630 deletions

View File

@@ -22,18 +22,38 @@ logger = setup_logger(
console=True
)
# 定义日志函数
def log_info(message):
logger.info(message)
def log_warning(message):
logger.warning(message)
def log_error(message):
logger.error(message)
def log_warning(message, **kwargs):
"""警告信息,黄色显示"""
logger.warning(f"\033[33m{message}\033[0m", **kwargs)
def log_trigger(message):
logger.info(f"TRIGGER: {message}")
def log_failure(message, **kwargs):
"""失败信息,红色显示"""
logger.error(f"\033[31m{message}\033[0m", **kwargs)
# 定义带颜色的日志函数
def log_success(message, **kwargs):
"""成功信息,绿色显示"""
logger.info(f"\033[32m{message}\033[0m", **kwargs)
def log_failure(message, **kwargs):
"""失败信息,红色显示"""
logger.error(f"\033[31m{message}\033[0m", **kwargs)
def log_process(message, **kwargs):
"""处理中信息,黄色显示"""
logger.info(f"\033[33m{message}\033[0m", **kwargs)
def log_result(message, **kwargs):
"""结果信息,蓝色显示"""
logger.info(f"\033[34m{message}\033[0m", **kwargs)
def log_message(message, **kwargs):
"""普通信息,白色显示"""
logger.info(f"\033[37m{message}\033[0m", **kwargs)
# 数据库相关导入
import pymysql
from sqlalchemy import create_engine, text
@@ -120,10 +140,10 @@ class AccountManager:
return account
except (ConnectionError, TimeoutError) as e:
# 网络连接相关异常
log_error(f"账户网络连接异常: {str(e)}")
log_failure(f"账户网络连接异常: {str(e)}")
except Exception as e:
# 其他异常
log_error(f"获取账户异常: {str(e)}")
log_failure(f"获取账户异常: {str(e)}")
# 无论什么异常,都尝试下一个账户
self.current_index = (self.current_index + 1) % len(self.accounts)
@@ -132,7 +152,7 @@ class AccountManager:
retry_count = getattr(self, '_retry_count', 0)
if retry_count >= len(self.accounts) * 2: # 尝试每个账户2次
setattr(self, '_retry_count', 0)
log_error("所有账户都尝试失败,返回最后一个账户作为尝试")
log_failure("所有账户都尝试失败,返回最后一个账户作为尝试")
return self.accounts[0] # 返回第一个账户作为最后的尝试
setattr(self, '_retry_count', retry_count + 1)
@@ -165,9 +185,9 @@ class DataDownloader:
}
progress = current / total * 100
elapsed = time.time() - start_time
print(f"\r处理中: {progress:.1f}% ({current}/{total}) | 耗时: {elapsed:.1f}s", end='', flush=True)
log_process(f"\r处理中: {progress:.1f}% ({current}/{total}) | 耗时: {elapsed:.1f}s", end='', flush=True)
if current == total:
print("\n数据处理完成!") # 添加换行符和完成信息
log_success(f"\n数据处理完成!") # 添加换行符和完成信息
# def show_progress(self, current, total, start_time):
# """更频繁的进度显示"""
@@ -201,7 +221,7 @@ class DataDownloader:
cursor.close()
del self.conn_pool[conn_id]
self.active_connections.add(conn_id)
log_info(f"复用数据库连接 {conn_id}")
log_message(f"复用数据库连接 {conn_id}")
self.db_conn = conn
return True
except Exception:
@@ -231,7 +251,7 @@ class DataDownloader:
self.active_connections.remove(active_conn_id)
# 添加到连接池
self.conn_pool[conn_id] = conn
log_info(f"连接已返回连接池: {conn_id}")
log_message(f"连接已返回连接池: {conn_id}")
return True
except Exception:
# 连接已无效,关闭它
@@ -239,7 +259,7 @@ class DataDownloader:
conn.close()
if hasattr(self, 'conn_count'):
self.conn_count = max(0, self.conn_count - 1)
log_info(f"无效连接已关闭,当前连接数: {self.conn_count}")
log_message(f"无效连接已关闭,当前连接数: {self.conn_count}")
except:
pass
return False
@@ -258,7 +278,7 @@ class DataDownloader:
# 添加到连接池
self.conn_pool[conn_id] = self.db_conn
self.db_conn = None # 清除对象属性
log_info(f"默认连接已返回连接池: {conn_id}")
log_message(f"默认连接已返回连接池: {conn_id}")
return True
except Exception:
# 连接已无效,关闭它
@@ -266,7 +286,7 @@ class DataDownloader:
self.db_conn.close()
self.db_conn = None
self.conn_count = max(0, self.conn_count - 1)
log_info(f"默认无效连接已关闭,当前连接数: {self.conn_count}")
log_message(f"默认无效连接已关闭,当前连接数: {self.conn_count}")
except:
pass
return False
@@ -284,7 +304,7 @@ class DataDownloader:
conn_id = f"conn_{int(time.time() * 1000)}"
self.conn_count += 1
self.active_connections.add(conn_id)
log_info(f"创建数据库连接 {conn_id},当前连接数: {self.conn_count}")
log_message(f"创建数据库连接 {conn_id},当前连接数: {self.conn_count}")
self.db_conn = conn
return True # 静默返回,不记录成功日志
except pymysql.err.OperationalError as e:
@@ -301,24 +321,24 @@ class DataDownloader:
temp_conn.commit()
temp_conn.close()
log_info(f"数据库 {Config.DB_CONFIG['database']} 创建成功")
log_message(f"数据库 {Config.DB_CONFIG['database']} 创建成功")
# 重新尝试连接
continue
except Exception as create_e:
log_error(f"创建数据库失败: {create_e}")
log_failure(f"创建数据库失败: {create_e}")
return False
elif attempt < Config.DB_MAX_RETRIES - 1: # 不是最后一次尝试
log_warning(f"数据库连接失败(尝试 {attempt + 1}/{Config.DB_MAX_RETRIES}): {e}")
time.sleep(Config.DB_RETRY_INTERVAL)
else:
log_error(f"数据库连接失败,已达到最大重试次数 {Config.DB_MAX_RETRIES}")
log_failure(f"数据库连接失败,已达到最大重试次数 {Config.DB_MAX_RETRIES}")
return False
except Exception as e:
if attempt < Config.DB_MAX_RETRIES - 1: # 不是最后一次尝试
log_warning(f"数据库连接失败(尝试 {attempt + 1}/{Config.DB_MAX_RETRIES}): {e}")
time.sleep(Config.DB_RETRY_INTERVAL)
else:
log_error(f"数据库连接失败,已达到最大重试次数 {Config.DB_MAX_RETRIES}")
log_failure(f"数据库连接失败,已达到最大重试次数 {Config.DB_MAX_RETRIES}")
return False
def update_to_db(self, data: pd.DataFrame, table_name: str):
@@ -350,7 +370,7 @@ class DataDownloader:
# 添加表结构检查
if table_name.startswith('stock_'):
if 'trade_date' not in data.columns:
log_error(f"数据缺少必要列 trade_date")
log_failure(f"数据缺少必要列 trade_date")
return False
if not pd.api.types.is_datetime64_any_dtype(data['trade_date']):
data['trade_date'] = pd.to_datetime(data['trade_date'], format='%Y%m%d')
@@ -359,30 +379,30 @@ class DataDownloader:
data.to_sql(table_name, connection, if_exists='append', index=False, chunksize=1000)
return True
except Exception as e:
log_error(f"数据库更新失败: {e}")
log_failure(f"数据库更新失败: {e}")
return False
finally:
# 确保资源释放
if engine:
try:
engine.dispose()
log_info(f"SQLAlchemy引擎已释放")
log_message(f"SQLAlchemy引擎已释放")
except Exception as e:
log_error(f"释放SQLAlchemy引擎失败: {e}")
log_failure(f"释放SQLAlchemy引擎失败: {e}")
def update_database(self, progress_queue=None, completion_label=None):
try:
print("开始更新数据库...")
log_process("开始更新数据库...")
start_time = time.time()
# 1. 更新股票代码表(使用缓存机制)
print("正在更新股票代码表...")
log_process("正在更新股票代码表...")
df_all = self.account_manager.get_stock_basic(force_primary=True)
self.update_to_db(df_all, 'stock_basic')
# 2. 从本地文件更新日线数据
print("正在更新个股日线数据...")
log_process("正在更新个股日线数据...")
files = [f for f in os.listdir(Config.OUTPUT_DIR) if f.endswith('_daily_data.txt')]
self.total_files = len(files)
self.processed_files = 0
@@ -391,7 +411,7 @@ class DataDownloader:
# 使用批量插入优化数据库写入
self.batch_update_stock_data(files, start_time)
else:
print("没有找到需要处理的个股数据文件")
log_message("没有找到需要处理的个股数据文件")
# 初始显示进度0%
# self.show_progress(0, self.total_files, start_time)
@@ -410,7 +430,7 @@ class DataDownloader:
# self.show_progress(i, self.total_files, start_time)
# 3. 更新指数数据(从本地文件)
print("正在更新指数数据...")
log_process("正在更新指数数据...")
index_file = os.path.join(Config.INDEX_DIR, '000001.SH.txt')
if os.path.exists(index_file):
df_index = self.read_from_txt(index_file)
@@ -418,7 +438,7 @@ class DataDownloader:
self.update_to_db(df_index, 'index_daily')
# 4. 更新个股数据(分批处理)
print("正在更新实时个股数据...")
log_process("正在更新实时个股数据...")
stock_codes = df_all['ts_code'].tolist()
batch_size = 400
for i in range(0, len(stock_codes), batch_size):
@@ -426,14 +446,14 @@ class DataDownloader:
df = self.fetch_data_with_retry(pro.daily, ts_code=','.join(batch))
if df is not None:
self.update_to_db(df, 'stock_daily')
print(f"数据库更新完成! 总耗时: {time.time() - start_time:.2f}")
log_success(f"数据库更新完成! 总耗时: {time.time() - start_time:.2f}")
if completion_label:
completion_label.config(text="数据库更新完成!", foreground="green")
if progress_queue:
progress_queue.put(1)
return True
except Exception as e:
log_error(f"数据库更新失败: {e}")
log_failure(f"数据库更新失败: {e}")
if completion_label:
completion_label.config(text="数据库更新失败!", foreground="red")
return False
@@ -489,15 +509,15 @@ class DataDownloader:
self.show_progress(total_files, total_files, start_time)
except Exception as e:
log_error(f"批量更新股票数据失败: {e}")
log_failure(f"批量更新股票数据失败: {e}")
finally:
# 确保资源释放
if engine:
try:
engine.dispose()
log_info(f"批量更新的SQLAlchemy引擎已释放")
log_message(f"批量更新的SQLAlchemy引擎已释放")
except Exception as e:
log_error(f"释放批量更新的SQLAlchemy引擎失败: {e}")
log_failure(f"释放批量更新的SQLAlchemy引擎失败: {e}")
def create_dirs(self):
# 创建基础目录
@@ -543,7 +563,7 @@ class DataDownloader:
# logging.info(f"数据已保存到 {filename}")
return True
except Exception as e:
log_error(f"保存文件时出错: {e}")
log_failure(f"保存文件时出错: {e}")
return False
def process_stock_code(self, code, progress_queue=None): # 修改参数默认值为None
@@ -614,13 +634,13 @@ class DataDownloader:
if progress_queue is not None: # 添加判断
progress_queue.put(1)
except (ConnectionError, TimeoutError) as e:
log_error(f"股票 {code} 网络连接异常: {str(e)}")
log_failure(f"股票 {code} 网络连接异常: {str(e)}")
except pd.errors.EmptyDataError:
log_warning(f"股票 {code} 返回空数据")
except (pymysql.err.OperationalError, pymysql.err.InterfaceError) as e:
log_error(f"股票 {code} 数据库操作异常: {str(e)}")
log_failure(f"股票 {code} 数据库操作异常: {str(e)}")
except Exception as e:
log_error(f"处理股票代码 {code} 时出错: {str(e)}")
log_failure(f"处理股票代码 {code} 时出错: {str(e)}")
finally:
if progress_queue is not None: # 添加判断
progress_queue.put(1)
@@ -642,7 +662,7 @@ class DataDownloader:
progress_queue.put(1)
return True
except Exception as e:
log_error(f"Error fetching index data for {ts_code}: {e}")
log_failure(f"Error fetching index data for {ts_code}: {e}")
if completion_label:
completion_label.config(text="获取指数数据失败!", foreground="red")
return False
@@ -653,7 +673,7 @@ class DataDownloader:
df_all = self.account_manager.get_stock_basic(force_primary=True)
df_all[['ts_code', 'name']].to_csv(Config.INPUT_FILE, index=False, header=False, sep='\t')
log_info(f'所有资料已保存到 {Config.INPUT_FILE}')
log_success(f'所有资料已保存到 {Config.INPUT_FILE}')
if completion_label:
completion_label.config(text="代码表更新完成!", foreground="green")
if progress_queue:
@@ -662,6 +682,41 @@ class DataDownloader:
logging.error(f'发生错误: {e}')
if completion_label:
completion_label.config(text="更新代码表失败!", foreground="red")
def fetch_and_save_stock_basic_info(self):
"""获取个股基础信息并保存到baseinfo目录"""
try:
log_process("开始获取个股基础信息...")
# 获取Tushare API实例
pro = self.account_manager.get_next_account(force_primary=True) # 强制使用主账户
# 获取股票基础信息
df = pro.stock_basic(
exchange='', # 交易所代码,不指定则查询所有
list_status='L', # 上市状态L上市D退市P暂停上市
fields='ts_code,symbol,name,area,industry,fullname,enname,market,exchange,curr_type,list_status,list_date,delist_date,is_hs'
)
if df is None or df.empty:
log_failure("未获取到个股基础信息")
return False
# 创建baseinfo目录
baseinfo_dir = os.path.join(Config.BASE_DIR, 'baseinfo')
os.makedirs(baseinfo_dir, exist_ok=True)
# 保存数据到文件使用TXT格式而不是CSV
output_file = os.path.join(baseinfo_dir, 'stock_basic_info.txt')
self.save_to_txt(df, output_file)
log_success(f"个股基础信息已成功保存到: {output_file}")
log_result(f"共获取到 {len(df)} 条个股基础信息")
return True
except Exception as e:
log_failure(f"获取个股基础信息失败: {str(e)}")
log_failure(f"获取个股基础信息失败: {str(e)}")
return False
def process_stock_codes_batch(self, codes: list):
"""批量处理股票代码(每次最多4000个)"""
@@ -700,35 +755,35 @@ class DataDownloader:
log_warning(f"数据库更新失败,但文件已保存: {code}")
else:
failed_count += 1
log_error(f"文件保存失败: {code}")
log_failure(f"文件保存失败: {code}")
else:
log_warning(f"未获取到股票 {code} 的数据")
failed_count += 1
except Exception as code_e:
failed_count += 1
log_error(f"处理股票 {code} 时出错: {str(code_e)}")
log_failure(f"处理股票 {code} 时出错: {str(code_e)}")
else:
failed_count += len(batch)
log_warning(f"批次 {batch_idx//batch_size + 1}/{total_batches} 返回空数据")
# 显示批次进度
log_info(f"批次 {batch_idx//batch_size + 1}/{total_batches} 处理完成 | 成功: {processed_count} | 失败: {failed_count}")
log_result(f"批次 {batch_idx//batch_size + 1}/{total_batches} 处理完成 | 成功: {processed_count} | 失败: {failed_count}")
except (ConnectionError, TimeoutError) as e:
failed_count += len(batch)
log_error(f"批次 {batch_idx//batch_size + 1} 网络异常: {str(e)}")
log_failure(f"批次 {batch_idx//batch_size + 1} 网络异常: {str(e)}")
# 更换账户后重试
pro = self.account_manager.get_next_account()
except Exception as batch_e:
failed_count += len(batch)
log_error(f"批次 {batch_idx//batch_size + 1} 处理异常: {str(batch_e)}")
log_failure(f"批次 {batch_idx//batch_size + 1} 处理异常: {str(batch_e)}")
# 记录总体处理结果
log_info(f"批量处理完成 | 总成功: {processed_count} | 总失败: {failed_count}")
log_result(f"批量处理完成 | 总成功: {processed_count} | 总失败: {failed_count}")
return {'success': processed_count, 'failed': failed_count}
except Exception as e:
log_error(f"批量处理主流程失败: {str(e)}")
log_failure(f"批量处理主流程失败: {str(e)}")
return {'success': processed_count, 'failed': failed_count + (len(codes) - processed_count - failed_count)}
finally:
# 确保所有连接正确管理
@@ -737,9 +792,9 @@ class DataDownloader:
if hasattr(self, 'db_conn') and self.db_conn:
self.db_conn.close()
self.db_conn = None
log_info("数据库连接已关闭")
log_message("数据库连接已关闭")
except Exception as e:
log_error(f"关闭数据库连接时出错: {e}")
log_failure(f"关闭数据库连接时出错: {e}")
def read_from_txt(self, file_path):
"""从TXT文件读取行情数据 - 内存优化版本"""
@@ -759,7 +814,7 @@ class DataDownloader:
else:
return pd.DataFrame()
except Exception as e:
log_error(f"读取文件 {file_path} 失败: {e}")
log_failure(f"读取文件 {file_path} 失败: {e}")
return None
# 添加新的方法用于优化数据库存储
@@ -784,18 +839,18 @@ class DataDownloader:
WHERE s1.id > s2.id AND s1.trade_date = s2.trade_date AND s1.stock_code = s2.stock_code
"""))
log_info("数据库存储优化完成")
log_message("数据库存储优化完成")
return True
except Exception as e:
log_error(f"数据库存储优化失败: {e}")
log_failure(f"数据库存储优化失败: {e}")
return False
finally:
if engine:
try:
engine.dispose()
log_info(f"SQLAlchemy引擎已释放")
log_message(f"SQLAlchemy引擎已释放")
except Exception as e:
log_error(f"释放SQLAlchemy引擎失败: {e}")
log_failure(f"释放SQLAlchemy引擎失败: {e}")
class ConsoleDataDownloader:
"""控制台模式数据下载器"""
@@ -807,10 +862,10 @@ class ConsoleDataDownloader:
"""使用指定的选项运行"""
try:
if choice == '0':
print("程序正在退出...")
log_message("程序正在退出...")
sys.exit(0)
if choice == '6': # 新增全部工作逻辑
print("开始执行全部工作...")
log_process("开始执行全部工作...")
start_time = time.time()
# 1. 更新股票代码
@@ -824,15 +879,15 @@ class ConsoleDataDownloader:
# 4. 同步数据库
if not self.downloader.connect_db():
print("\033[31m数据库连接失败,跳过数据库同步\033[0m")
log_failure("数据库连接失败,跳过数据库同步")
else:
self.downloader.update_database()
print(f"全部工作完成! 总耗时: {time.time() - start_time:.2f}")
log_success(f"全部工作完成! 总耗时: {time.time() - start_time:.2f}")
return
# 检查数据库连接是否正常(仅当选择数据库相关操作时)
if choice == '5' and not self.downloader.connect_db():
print("\033[31m数据库连接失败,请检查配置后重试\033[0m")
log_failure("数据库连接失败,请检查配置后重试")
return
# 处理选项7拷贝数据
@@ -844,6 +899,11 @@ class ConsoleDataDownloader:
if choice == '8':
self.check_data_integrity()
return
# 处理选项9获取个股基础信息
if choice == '9':
self.downloader.fetch_and_save_stock_basic_info()
return
tasks = []
if choice in ('1', '4'):
@@ -856,17 +916,17 @@ class ConsoleDataDownloader:
tasks.append(self.downloader.update_database)
if not tasks:
print("无效选项")
log_warning("无效选项")
return
print("开始执行任务...")
log_process("开始执行任务...")
start_time = time.time()
for task in tasks:
task()
print(f"任务完成! 总耗时: {time.time() - start_time:.2f}")
log_success(f"任务完成! 总耗时: {time.time() - start_time:.2f}")
except Exception as e:
log_error(f"执行任务时发生异常: {e}")
print(f"\033[31m执行任务时发生异常: {e}\033[0m")
log_failure(f"执行任务时发生异常: {e}")
log_failure(f"执行任务时发生异常: {e}")
def run(self):
try:
@@ -879,6 +939,7 @@ class ConsoleDataDownloader:
print("6. 全部工作(下载+同步数据库)") # 新增选项
print("7. 拷贝更新后的数据到目标目录") # 新增选项
print("8. 检查数据完整性") # 新增选项
print("9. 获取个股基础信息") # 新增选项
print("0. 退出")
while True:
@@ -889,10 +950,10 @@ class ConsoleDataDownloader:
print("\n程序已中断")
return
except Exception as e:
log_error(f"用户交互过程中发生异常: {e}")
print(f"\033[31m操作异常: {e}\033[0m")
log_failure(f"用户交互过程中发生异常: {e}")
log_failure(f"操作异常: {e}")
if choice == '6': # 新增全部工作逻辑
print("开始执行全部工作...")
log_process("开始执行全部工作...")
start_time = time.time()
# 1. 更新股票代码
@@ -906,16 +967,16 @@ class ConsoleDataDownloader:
# 4. 同步数据库
if not self.downloader.connect_db():
print("\033[31m数据库连接失败,跳过数据库同步\033[0m")
log_failure("数据库连接失败,跳过数据库同步")
else:
self.downloader.update_database()
print(f"全部工作完成! 总耗时: {time.time() - start_time:.2f}")
log_success(f"全部工作完成! 总耗时: {time.time() - start_time:.2f}")
continue
# 检查数据库连接是否正常(仅当选择数据库相关操作时)
# 检查数据库连接是否正常(仅当选择数据库相关操作时)
if choice == '5' and not self.downloader.connect_db():
print("\033[31m数据库连接失败,请检查配置后重试\033[0m")
log_failure("数据库连接失败,请检查配置后重试")
continue
# 处理选项7拷贝数据
@@ -934,10 +995,10 @@ class ConsoleDataDownloader:
tasks.append(self.downloader.update_database)
if not tasks:
print("无效选项,请重新输入")
log_warning("无效选项,请重新输入")
continue
print("开始执行任务...")
log_process("开始执行任务...")
start_time = time.time()
completed = 0
@@ -948,24 +1009,23 @@ class ConsoleDataDownloader:
completed += 1
except (ConnectionError, TimeoutError) as e:
failed += 1
log_error(f"任务执行网络异常: {str(e)}")
log_failure(f"任务执行网络异常: {str(e)}")
except Exception as e:
failed += 1
log_error(f"任务执行异常: {str(e)}")
log_failure(f"任务执行异常: {str(e)}")
print(f"任务完成! 成功: {completed}, 失败: {failed}, 总耗时: {time.time()-start_time:.2f}")
log_success(f"任务完成! 成功: {completed}, 失败: {failed}, 总耗时: {time.time()-start_time:.2f}")
except (ConnectionError, TimeoutError) as e:
log_error(f"用户交互过程中发生网络异常: {str(e)}")
print(f"网络异常: {str(e)}")
log_failure(f"用户交互过程中发生网络异常: {str(e)}")
log_failure(f"网络异常: {str(e)}")
except Exception as e:
log_error(f"用户交互过程中发生异常: {str(e)}")
print(f"操作异常: {str(e)}")
log_failure(f"用户交互过程中发生异常: {str(e)}")
log_failure(f"操作异常: {str(e)}")
except KeyboardInterrupt:
log_warning("用户中断程序")
print("程序已中断")
except Exception as e:
log_error(f"程序运行异常: {str(e)}")
print(f"程序运行异常: {str(e)}")
log_failure(f"程序运行异常: {str(e)}")
def copy_data_to_target_directory(self):
"""拷贝D:\gp_data\day目录下的数据到D:\gp_data\floor_cl\data目录"""
@@ -977,17 +1037,17 @@ class ConsoleDataDownloader:
source_dir = os.path.join(Config.BASE_DIR, 'day')
target_dir = 'D:\\data\\jq_hc\\data'
print(f"准备从 {source_dir} 拷贝数据到 {target_dir}")
log_process(f"准备从 {source_dir} 拷贝数据到 {target_dir}")
try:
# 确保目标目录存在
if not os.path.exists(target_dir):
os.makedirs(target_dir, exist_ok=True)
print(f"已创建目标目录: {target_dir}")
log_message(f"已创建目标目录: {target_dir}")
# 检查源目录是否存在
if not os.path.exists(source_dir):
print(f"\033[31m源目录 {source_dir} 不存在,请检查路径\033[0m")
log_failure(f"源目录 {source_dir} 不存在,请检查路径")
return
# 获取源目录中的txt文件列表
@@ -995,10 +1055,10 @@ class ConsoleDataDownloader:
total_files = len(files)
if total_files == 0:
print("\033[33m源目录中没有找到文件\033[0m")
log_warning("源目录中没有找到文件")
return
print(f"开始拷贝 {total_files} 个文件...")
log_process(f"开始拷贝 {total_files} 个文件...")
start_time = time.time()
copied_count = 0
failed_count = 0
@@ -1015,21 +1075,21 @@ class ConsoleDataDownloader:
# 显示进度
progress = (i / total_files) * 100
elapsed = time.time() - start_time
print(f"\r进度: [{('#' * int(progress / 2)):50}] {progress:.1f}% | {file_name}", end='', flush=True)
log_process(f"\r进度: [{('#' * int(progress / 2)):50}] {progress:.1f}% | {file_name}", end='', flush=True)
except Exception as e:
failed_count += 1
print(f"\n\033[31m拷贝文件 {file_name} 失败: {str(e)}\033[0m")
log_failure(f"\n拷贝文件 {file_name} 失败: {str(e)}")
# 打印完成信息
print(f"\n拷贝完成!")
print(f"成功: {copied_count}, 失败: {failed_count}")
print(f"总耗时: {time.time() - start_time:.2f}")
log_success(f"\n拷贝完成!")
log_result(f"成功: {copied_count}, 失败: {failed_count}")
log_result(f"总耗时: {time.time() - start_time:.2f}")
except KeyboardInterrupt:
print("\n\033[33m拷贝操作已被用户中断\033[0m")
log_warning("\n拷贝操作已被用户中断")
except Exception as e:
print(f"\n\033[31m拷贝过程中发生异常: {str(e)}\033[0m")
log_failure(f"\n拷贝过程中发生异常: {str(e)}")
def check_data_integrity(self):
"""检查数据完整性运行check_market_data.py程序"""
@@ -1037,7 +1097,7 @@ class ConsoleDataDownloader:
import os
try:
print("开始检查数据完整性...")
log_process("开始检查数据完整性...")
# 构建check_market_data.py的完整路径
script_path = os.path.join(os.path.dirname(__file__), 'check_market_data.py')
@@ -1051,14 +1111,14 @@ class ConsoleDataDownloader:
)
if result.returncode == 0:
print(f"\n\033[32m数据完整性检查完成!\033[0m")
log_success(f"\n数据完整性检查完成!")
else:
print(f"\n\033[31m数据完整性检查失败,返回码: {result.returncode}\033[0m")
log_failure(f"\n数据完整性检查失败,返回码: {result.returncode}")
except FileNotFoundError:
print(f"\n\033[31m未找到check_market_data.py文件请检查路径:\033[0m {script_path}")
log_failure(f"\n未找到check_market_data.py文件请检查路径: {script_path}")
except Exception as e:
print(f"\n\033[31m执行数据完整性检查时发生异常: {e}\033[0m")
log_failure(f"\n执行数据完整性检查时发生异常: {e}")
def process_stock_codes(self):
"""处理所有股票代码 - 优化版本"""
@@ -1066,7 +1126,7 @@ class ConsoleDataDownloader:
with open(Config.INPUT_FILE, 'r', encoding='utf-8') as f:
stock_codes = [line.strip().split('\t')[0] for line in f if line.strip()]
except Exception as e:
log_error(f"读取股票代码文件失败: {e}")
log_failure(f"读取股票代码文件失败: {e}")
return
total = len(stock_codes)
@@ -1077,7 +1137,7 @@ class ConsoleDataDownloader:
progress = (completed / total) * 100
elapsed = time.time() - start_time
# 单行进度显示
print(
log_process(
f"\r进度: [{'#' * int(progress / 2)}{' ' * (50 - int(progress / 2))}] {progress:.1f}% | 已完成: {completed}/{total} | 耗时: {elapsed:.1f}s",
end='', flush=True)
@@ -1098,8 +1158,8 @@ class ConsoleDataDownloader:
print_progress()
future.result()
print(f"\r进度: [{'#' * 50}] 100.0% | 已完成: {total}/{total} | 总耗时: {time.time() - start_time:.1f}s")
print(f"\n个股数据下载完成!")
log_success(f"\r进度: [{('#' * 50)}] 100.0% | 已完成: {total}/{total} | 总耗时: {time.time() - start_time:.1f}s")
log_success(f"\n个股数据下载完成!")
# 创建全局AccountManager实例
_global_account_manager = AccountManager()
@@ -1113,8 +1173,8 @@ def get_cached_data(code):
def parse_args():
"""解析命令行参数"""
parser = argparse.ArgumentParser(description='Tushare数据更新工具')
parser.add_argument('-c', '--choice', type=str, choices=['0', '1', '2', '3', '4', '5', '6', '7', '8'],
help='要执行的操作: 0=退出, 1=更新股票代码表, 2=更新指数数据, 3=更新个股数据, 4=全部更新, 5=同步数据库, 6=全部工作, 7=拷贝数据到目标目录, 8=检查数据完整性')
parser.add_argument('-c', '--choice', type=str, choices=['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
help='要执行的操作: 0=退出, 1=更新股票代码表, 2=更新指数数据, 3=更新个股数据, 4=全部更新, 5=同步数据库, 6=全部工作, 7=拷贝数据到目标目录, 8=检查数据完整性, 9=获取个股基础信息')
return parser.parse_args()
if __name__ == "__main__":