import baostock as bs import pandas as pd from datetime import datetime, timedelta import time import logging import sys # 配置logging模块 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') # 登录baostock系统 def login_baostock(): lg = bs.login() if lg.error_code != '0': print("登录失败:", lg.error_msg) exit() return lg # 获取最近一个交易日 def get_last_trade_date(): today = datetime.now().strftime("%Y-%m-%d") trade_dates = bs.query_trade_dates(start_date=today, end_date=today).get_data() if trade_dates.empty or trade_dates.iloc[0]['is_trading_day'] == '0': # 如果当天不是交易日,向前推算 last_trade_date = bs.query_trade_dates( start_date=(datetime.now() - timedelta(days=10)).strftime("%Y-%m-%d"), end_date=today ).get_data() last_trade_date = last_trade_date[last_trade_date['is_trading_day'] == '1'].iloc[-1]['calendar_date'] else: last_trade_date = today return last_trade_date # 获取当前日期的前一个交易日 def get_last_trade_date_yesterday(): # 获取昨天的日期 yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d') trade_dates = bs.query_trade_dates(start_date=yesterday, end_date=yesterday).get_data() if trade_dates.empty or trade_dates.iloc[0]['is_trading_day'] == '0': # 如果当天不是交易日,向前推算 last_trade_date = bs.query_trade_dates( start_date=(datetime.now() - timedelta(days=10)).strftime("%Y-%m-%d"), end_date=yesterday ).get_data() last_trade_date = last_trade_date[last_trade_date['is_trading_day'] == '1'].iloc[-1]['calendar_date'] else: last_trade_date = yesterday return last_trade_date def is_before_6pm(): now = datetime.now() current_hour = now.hour return current_hour < 18 def get_trade_date(): if is_before_6pm(): # 小于当天18点,取前一天。baostock库交易日18时之前,日 K 数据还未入库,所以找前一天。 last_trade_date = get_last_trade_date_yesterday() else: # 大于当天18点,交易日取前当天 last_trade_date = get_last_trade_date() return last_trade_date # 获取所有A股股票代码(剔除指数代码) def get_all_stock_codes(last_trade_date): rs = bs.query_all_stock(day=last_trade_date) # 使用最近一个交易日获取股票列表 stock_list = [] while (rs.error_code == '0') & rs.next(): stock_code = rs.get_row_data()[0] # 股票代码格式如:sh.600000 # 剔除指数代码(指数代码通常以 sh.000 或 sz.399 开头) if not (stock_code.startswith("sh.000") or stock_code.startswith("sz.399")): stock_list.append(stock_code) return stock_list # 获取股票名称 def get_stock_name(stock_code): stock_info = bs.query_stock_basic(code=stock_code).get_data() if stock_info.empty: return None if 'code_name' in stock_info.columns: # baostock返回的字段可能是code_name return stock_info.iloc[0]['code_name'] elif 'stock_name' in stock_info.columns: # 也可能是stock_name return stock_info.iloc[0]['stock_name'] return None # 剔除不符合条件的股票(北交所、科创板、ST股票) def filter_stock(stock_code): # 剔除北交所股票(代码以 bj. 开头) if stock_code.startswith("bj."): return False # 剔除科创板股票(代码以 sh.688 开头) if stock_code.startswith("sh.688"): return False # 获取股票名称并剔除ST股票 stock_name = get_stock_name(stock_code) # print(f"stock_name:{stock_name}") if stock_name is None: return False if "ST" in stock_name or "*ST" in stock_name: return False return True # 获取前第4个交易日 def get_before_trade_date_by_loop(today_date): dates = [] # 初始化变量i i = 0 before_trade_date = today_date # 设置循环的结束条件 while i < 3: # 执行循环体内的代码 before_trade_date = get_before_trade_date(before_trade_date) # print(i) # i自加 i += 1 dates.append(before_trade_date) return dates # 计算最近四个交易日的成交量是否符合条件 def calculate_volume_multiple(stock_code, last_trade_date): # 获取最近四个交易日的日期 trade_dates = bs.query_trade_dates( start_date=(datetime.strptime(last_trade_date, "%Y-%m-%d") - timedelta(days=10)).strftime("%Y-%m-%d"), end_date=last_trade_date ).get_data() trade_dates = trade_dates[trade_dates['is_trading_day'] == '1'].tail(4)['calendar_date'].tolist() if len(trade_dates) < 4: logging.warning(f"Not enough trading days for {stock_code}") return None # 查询最近四个交易日的成交量 k_data = bs.query_history_k_data_plus( code=stock_code, fields="date,volume", start_date=trade_dates[0], end_date=last_trade_date, frequency="d", adjustflag="3" ).get_data() if k_data.empty or len(k_data) < 4: logging.warning(f"No data or insufficient data for {stock_code}") return None # 提取最近四个交易日的成交量数据 volumes = {} for date in trade_dates: date_data = k_data[k_data['date'] == date] if date_data.empty: logging.warning(f"Missing data for {stock_code} on {date}") return None volumes[date] = float(date_data.iloc[0]['volume']) # 检查成交量条件 if volumes[trade_dates[2]] / volumes[trade_dates[3]] < 3: return None if volumes[trade_dates[1]] < volumes[trade_dates[2]] * 0.8: return None if volumes[trade_dates[0]] < volumes[trade_dates[2]] * 0.8: return None # 获取股票名称 stock_name = get_stock_name(stock_code) if stock_name is None: logging.warning(f"Failed to get stock name for {stock_code}") return None return { "股票代码": stock_code, "股票名称": stock_name, "目标日期": last_trade_date, "倒数第三个交易日成交量": str(round(volumes[trade_dates[2]] / 1000000, 2)) + "万手", "倒数第二个交易日成交量": str(round(volumes[trade_dates[1]] / 1000000, 2)) + "万手", "最近一个交易日成交量": str(round(volumes[trade_dates[0]] / 1000000, 2)) + "万手" } # 计算成交量放大倍数 def calculate_volume_multiple_last4days(stock_code, today, yesterday, before, forth, iv_param, pcent): # 获取股票名称 stock_name = get_stock_name(stock_code) if stock_name is None: return None # print(f"calculate_volume_multiple ====== stock_code is {stock_code}") # 查询目标日期和之前5天的数据,一共6天的数据 k_data = bs.query_history_k_data_plus( code=stock_code, fields="date,volume,pctChg", start_date=forth, end_date=today, frequency="d", adjustflag="3" ).get_data() # print(f"calculate_volume_multiple ====== k_data is {k_data}") # 检查是否有数据,以及成交量是否为空,数据天数不少于4天 if k_data.empty or len(k_data) < 4: logging.warning(f"No data or insufficient data for {stock_code}") return None # 跳过无数据或停牌的股票 # 提取今天的成交量数据 today_data = k_data[k_data['date'] == today] # 提取昨天的成交量数据 yesterday_data = k_data[k_data['date'] == yesterday] # 提取前天的成交量数据 before_data = k_data[k_data['date'] == before] # 提取前三天的成交量数据 forth_data = k_data[k_data['date'] == forth] # 检查今天和昨天的成交量数据是否有效 if today_data.empty or yesterday_data.empty or before_data.empty or forth_data.empty: logging.warning( f"Missing data for {stock_code}: start_date={forth}, before={before}, yesterday={yesterday}, end_date={today}") return None # 跳过数据缺失的情况 # 当日的数据 today_volume = today_data.iloc[0]['volume'] yesterday_volume = yesterday_data.iloc[0]['volume'] before_volume = before_data.iloc[0]['volume'] forth_volume = forth_data.iloc[0]['volume'] # 最近两日停牌的票 if today_volume == '' or yesterday_volume == '': logging.error(f"{stock_code}:停牌!") logging.error(f"Empty volume data for {stock_code}: yesterday={yesterday_volume}, today={today_volume}") logging.error(f"Empty volume data for {stock_code}: yesterday={yesterday_volume}, today={today_volume}") # 检查成交量是否为空字符串 if today_volume == '' or yesterday_volume == '' or before_volume == '' or forth_volume == '': logging.warning( f"Empty volume data for {stock_code}: forth={forth_volume}, before={before_volume}, yesterday={yesterday_volume}, today={today_volume}") return None # 跳过无效数据 # 将成交量转换为浮点数 try: current_volume = float(today_volume) yesterday_volume = float(yesterday_volume) before_volume = float(before_volume) forth_volume = float(forth_volume) except ValueError as e: logging.error(f"Failed to convert volume to float for {stock_code}: {e}") return None # 跳过无法转换的数据 # 计算成交量放大倍数 if forth_volume == 0: logging.warning(f"forth volume is zero for {stock_code}") return None # 避免除以零 before_volume_multiple = before_volume / forth_volume # 检查成交量条件 if before_volume_multiple < iv_param: return None if yesterday_volume < before_volume * pcent: return None if current_volume < yesterday_volume * pcent: return None current_date = today_data.iloc[0]['date'] # 涨跌幅(百分比) 日涨跌幅 = [(指定交易日的收盘价 - 指定交易日前收盘价) / 指定交易日前收盘价] * 100 % current_pctChg = today_data.iloc[0]['pctChg'] yesterday_pctChg = yesterday_data.iloc[0]['pctChg'] # volume 成交量(累计 单位:股) 万手要处理 6 位数 wanshou = "万手" forth_volume_string = str(round(forth_volume / 1000000, 2)) + wanshou before_volume_string = str(round(before_volume / 1000000, 2)) + wanshou yesterday_volume_string = str(round(yesterday_volume / 1000000, 2)) + wanshou current_volume_string = str(round(current_volume / 1000000, 2)) + wanshou return { "stock_code": stock_code, "stock_name": stock_name, "target_date": current_date, "forth_volume": forth_volume_string, "before_volume": before_volume_string, "yesterday_volume": yesterday_volume_string, "current_volume": current_volume_string, "compare_before_multiple": round(before_volume_multiple, 2), "current_pctChg": current_pctChg, "yesterday_pctChg": yesterday_pctChg, } # 获取前一天的交易日 def get_before_trade_date(today_date): # 获取昨天的日期 yesterday_date = (datetime.strptime(today_date, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d") # 检查昨天是否是交易日 trade_dates = bs.query_trade_dates(start_date=yesterday_date, end_date=yesterday_date).get_data() if trade_dates.empty or trade_dates.iloc[0]['is_trading_day'] == '0': print(f"yesterday_date is {yesterday_date}, {yesterday_date}不是交易日,往前迭代一天。") return get_before_trade_date(yesterday_date) else: return yesterday_date ####################################################### 主程序 ######################################################### ####################################################### 主程序 ######################################################### ####################################################### 主程序 ######################################################### def main(): argv = sys.argv print(f"完整python命令为:{argv},len(argv):{len(argv)}") # 放量倍数 iv_param = 3 # 今日是前一日的占比 pcent = 0.8 # 登录baostock login_baostock() logging.info("程序执行时间...小于当天18时,取前一个交易日,大与18时,采取当前天。") # 获取最近一个交易日 today = get_target_date_by_param(argv) dates = get_before_trade_date_by_loop(today) yesterday = dates[0] before = dates[1] forth = dates[2] print(f"today:{today}, yesterday:{dates[0]}, before:{dates[1]}, forth:{dates[2]}") # 获取所有A股股票代码(剔除指数代码) stock_list = get_all_stock_codes(today) # 使用切片来获取前十条元素 # 注意:如果列表长度小于10,这会返回整个列表 # first_ten = stock_list[:5] # print(first_ten) # 筛选符合条件的股票 result = [] for code in stock_list: if not filter_stock(code): continue volume_data = calculate_volume_multiple_last4days(code, today, yesterday, before, forth, iv_param, pcent) if volume_data: result.append(volume_data) # 将结果转换为DataFrame并排序 if result: df_result = pd.DataFrame(result) df_result = df_result.sort_values(by=["current_pctChg", "compare_before_multiple"], ascending=False) # 按最近一个交易日成交量倒序排序 # 输出到Excel文件 local_path = "D:/name/tools/bat/consecutive3days/files" output_file = f"{local_path}/{today}_成交量连续三天放大.xlsx" df_result.to_excel(output_file, index=False) print(f"结果已保存到文件: {output_file}") else: print("未找到符合条件的股票。") # 登出系统 bs.logout() def main3(): argv = sys.argv print(f"完整python命令为:{argv},len(argv):{len(argv)}") # 登录baostock login_baostock() logging.info("程序执行时间...小于当天18时,取前一个交易日,大与18时,采取当前天。") # 获取最近一个交易日 last_trade_date = get_target_date_by_param(argv) # 获取所有A股股票代码(剔除指数代码) stock_list = get_all_stock_codes(last_trade_date) # 筛选符合条件的股票 result = [] for code in stock_list: if not filter_stock(code): continue volume_data = calculate_volume_multiple(code, last_trade_date) if volume_data: result.append(volume_data) # 将结果转换为DataFrame并排序 if result: df_result = pd.DataFrame(result) df_result = df_result.sort_values(by=["最近一个交易日成交量"], ascending=False) # 按最近一个交易日成交量倒序排序 # 输出到Excel文件 local_path = "D:/name/tools/bat/volumeIncrease" output_file = f"{local_path}/{last_trade_date}_成交量放大条件股票.xlsx" df_result.to_excel(output_file, index=False) print(f"结果已保存到文件: {output_file}") else: print("未找到符合条件的股票。") # 登出系统 bs.logout() # 有输入日期,取日期的,默认取最近交易日的。 def get_target_date_by_param(argv): if len(argv) > 1 and argv[1] != '': logging.warning("当前取输入参数为目标日期! 注意日期输入格式 %Y-%m-%d !") target_date = argv[1] else: logging.warning("当前取最近交易日为目标日期! ") target_date = get_trade_date() return target_date def main2(): argv = sys.argv print(f"完整python命令为:{argv},len(argv):{len(argv)}") # 登录baostock login_baostock() target_date = get_target_date_by_param(argv) dates = get_before_trade_date_by_loop(target_date) # print(f"dates:{dates}") print(f"today:{target_date}, yesterday:{dates[0]}, before:{dates[1]}, forth:{dates[2]}") code = "sz.000488" filter_stock(code) # 程序入口 if __name__ == "__main__": # 记录开始时间 start_time = time.time() logging.info("程序开始执行...") logging.info("程序信息:计算股票成交量,相对昨天放量的情况!") main() # 记录另一条信息日志 logging.info("程序执行完成。") # 记录结束时间 end_time = time.time() # 计算并打印执行时长 logging.info(f"执行时长: {str(round((end_time - start_time) / 60, 2))} 分钟")
Python查询连续三天成交量较前5天平均值
于 2025-02-11 22:30:06 首次发布