Python查询连续三天成交量较前5天平均值

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))} 分钟")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值