AI-股票分析工具开发
一:背景
模型:除了各个大公司会搭建自己的私域模型,并且针对公司的知识做监督学习和强化学习,一般人使用的都是通用模型,需要依赖高质量的prompt的提高模型的准确率,减少幻觉
爱好:个人业余时间有限,又喜欢买股票,一般新知识的应用都会和股票分析工具搭上关系。
二:解决方案
提示词 + 模型[通义千问,deepseek,claude] + 人工调试
三:prompt提示词
我想写一个Python脚本,用来分析A股和深股的股票,具体需求如下: 1. 尽量使用akshare获取股票数据,如果有其他更好的库也可以 3. 筛选原则 3.1. 按照 "股票编号 股票名称 所属板块 分红率 市值 营收 利润" 找出各个板块的前三个龙头股,然后计算近三年的分红率,比如今年是2025年,计算2022-2024平均分红/当前价格 3.2. 从上述数据中,再把分红率大于3%的股票 单独组成一份数据 4、脚本添加必要的说明,介绍一下整个脚本的作用,已经如何运行脚本 5、代码中添加必要的打印 6、考虑异常退出,如果脚本出现异常终止的时候,需要输出终止原因 7、接口封装要好,层次清晰 8.获取数据的时候,如果太频繁会接口调用失败,需要考虑每只股票之间随机1~3s获取一次,如果获取失败,最多尝试三次,都失败之后,进行下一个股票的获取
四、模型原始产出
import akshare as ak import pandas as pd import time import random from datetime import datetime import traceback import numpy as np def main(): """ A股/深股龙头股分析脚本 功能: 1. 获取所有A股/深股股票数据 2. 按行业板块筛选市值前三的龙头股 3. 计算近三年平均分红率 4. 筛选分红率>3%的优质股票 输出两份报告: - 各板块龙头股报告 - 高分红率股票报告(分红率>3%) """ try: print("="*50) print("开始执行股票分析脚本") print(f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print("="*50) # 步骤1: 获取股票基本信息 print("\n[步骤1/4] 获取股票基本信息...") stock_basic_df = get_stock_basic_info() print(f"获取到 {len(stock_basic_df)} 只股票基本信息") # 步骤2: 获取财务数据 print("\n[步骤2/4] 获取财务数据(市值/营收/利润)...") financial_df = get_financial_data(stock_basic_df) # 步骤3: 获取分红数据 print("\n[步骤3/4] 获取分红数据...") dividend_df = get_dividend_data(stock_basic_df) # 步骤4: 数据整合与分析 print("\n[步骤4/4] 数据分析与筛选...") merged_df = merge_data(stock_basic_df, financial_df, dividend_df) # 按板块筛选龙头股 top_stocks = get_industry_top_stocks(merged_df) print("\n" + "="*50) print(f"各板块龙头股分析完成,共筛选出 {len(top_stocks)} 只龙头股") # 筛选高分红股票 high_dividend_stocks = top_stocks[top_stocks['分红率'] > 3] print(f"其中高分红率股票(>3%)有 {len(high_dividend_stocks)} 只") print("="*50 + "\n") # 输出结果 print_top_stocks(top_stocks) print_high_dividend_stocks(high_dividend_stocks) # 保存结果到CSV save_results(top_stocks, high_dividend_stocks) except Exception as e: print("\n" + "!"*50) print("脚本执行失败!错误信息:") print(traceback.format_exc()) print("!"*50) exit(1) def get_stock_basic_info(): """获取股票基本信息:代码、名称、所属行业""" print(" > 获取A股基本信息...") stock_info_a = ak.stock_info_a_code_name() print(" > 获取深股基本信息...") stock_info_sz = ak.stock_info_sz_name_code(indicator="A股列表") # 合并沪深两市股票 df = pd.concat([ stock_info_a[['code', 'name']], stock_info_sz[['A股代码', 'A股简称']].rename(columns={'A股代码': 'code', 'A股简称': 'name'}) ]).drop_duplicates('code') print(" > 获取行业分类信息...") industry_df = ak.stock_board_industry_name_em() industry_map = {} for _, row in industry_df.iterrows(): cons_df = ak.stock_board_industry_cons_em(symbol=row['板块名称']) for code in cons_df['代码']: industry_map[code] = row['板块名称'] # 添加行业信息 df['板块'] = df['code'].map(industry_map) return df.dropna(subset=['板块']) def get_financial_data(stock_basic_df): """获取财务数据:市值、营收、利润""" financial_data = [] total_stocks = len(stock_basic_df) processed = 0 failed = 0 print(f" > 开始获取财务数据,共 {total_stocks} 只股票...") for idx, code in enumerate(stock_basic_df['code']): processed += 1 if processed % 50 == 0: print(f" 已处理 {processed}/{total_stocks} 只股票,失败: {failed}") max_retries = 3 retry_count = 0 success = False while not success and retry_count < max_retries: try: # 添加随机延迟 (1-3秒) delay = random.uniform(1, 3) time.sleep(delay) # 获取最新指标数据 indicator_df = ak.stock_a_lg_indicator(symbol=code) if not indicator_df.empty: latest = indicator_df.iloc[0] financial_data.append({ 'code': code, '市值(亿)': latest['total_mv'] / 1e8, '营收(亿)': latest['or'] / 1e8, '利润(亿)': latest['netprofit'] / 1e8 }) success = True else: # 数据为空,视为失败 retry_count += 1 if retry_count < max_retries: print(f" {code} 财务数据为空,重试中... ({retry_count}/{max_retries})") time.sleep(2 ** retry_count) # 指数退避 except Exception as e: retry_count += 1 if retry_count < max_retries: print(f" {code} 财务数据获取失败,重试中... ({retry_count}/{max_retries}) 错误: {str(e)[:50]}") time.sleep(2 ** retry_count) # 指数退避 else: print(f" {code} 财务数据获取失败,跳过该股票") failed += 1 print(f" > 财务数据获取完成,成功: {len(financial_data)},失败: {failed}") return pd.DataFrame(financial_data) def get_dividend_data(stock_basic_df): """获取近三年分红数据并计算分红率""" dividend_data = [] current_year = datetime.now().year total_stocks = len(stock_basic_df) processed = 0 failed = 0 print(f" > 开始获取分红数据,共 {total_stocks} 只股票...") for idx, code in enumerate(stock_basic_df['code']): processed += 1 if processed % 50 == 0: print(f" 已处理 {processed}/{total_stocks} 只股票,失败: {failed}") max_retries = 3 retry_count = 0 success = False while not success and retry_count < max_retries: try: # 添加随机延迟 (1-3秒) delay = random.uniform(1, 3) time.sleep(delay) # 获取历史分红 div_df = ak.stock_history_dividend_detail(symbol=code, indicator="分红") if div_df.empty: # 没有分红数据,视为成功但跳过记录 success = True continue # 筛选近三年数据 div_df = div_df[div_df['公告日期'].str[:4].astype(int) >= current_year - 3] if not div_df.empty: avg_div = div_df['每股股息'].mean() # 获取当前股价 quote = ak.stock_zh_a_spot_em(symbol=code) if not quote.empty: current_price = quote['最新价'].values[0] div_rate = (avg_div / current_price * 100) if current_price > 0 else 0 dividend_data.append({ 'code': code, '平均分红': avg_div, '分红率': div_rate }) success = True else: # 股价获取失败 retry_count += 1 if retry_count < max_retries: print(f" {code} 股价获取失败,重试中... ({retry_count}/{max_retries})") time.sleep(2 ** retry_count) # 指数退避 else: # 近三年无分红数据,视为成功但跳过记录 success = True except Exception as e: retry_count += 1 if retry_count < max_retries: print(f" {code} 分红数据获取失败,重试中... ({retry_count}/{max_retries}) 错误: {str(e)[:50]}") time.sleep(2 ** retry_count) # 指数退避 else: print(f" {code} 分红数据获取失败,跳过该股票") failed += 1 print(f" > 分红数据获取完成,成功: {len(dividend_data)},失败: {failed}") return pd.DataFrame(dividend_data) def merge_data(basic_df, financial_df, dividend_df): """合并所有数据源""" merged = basic_df.merge(financial_df, on='code', how='left') merged = merged.merge(dividend_df, on='code', how='left') # 处理缺失值 merged['分红率'] = merged['分红率'].fillna(0) merged['平均分红'] = merged['平均分红'].fillna(0) # 过滤无效数据 merged = merged.dropna(subset=['市值(亿)', '营收(亿)', '利润(亿)']) merged = merged[merged['市值(亿)'] > 0] # 排除市值为0的股票 return merged def get_industry_top_stocks(df): """按行业筛选市值前三的龙头股""" top_stocks = [] for industry, group in df.groupby('板块'): # 排除无财务数据的股票 valid_group = group.dropna(subset=['市值(亿)']) if len(valid_group) == 0: continue # 按市值降序排序 sorted_group = valid_group.sort_values('市值(亿)', ascending=False) # 取前三名 top_three = sorted_group.head(3) top_stocks.append(top_three) return pd.concat(top_stocks).sort_values('板块') def print_top_stocks(df): """打印龙头股报告""" print("\n" + "="*50) print("各板块龙头股报告:") print("="*50) for industry, group in df.groupby('板块'): print(f"\n【{industry}】板块龙头股:") for _, row in group.iterrows(): print(f" {row['code']} {row['name']} | " f"市值:{row['市值(亿)']:.1f}亿 | " f"分红率:{row['分红率']:.2f}% | " f"营收:{row['营收(亿)']:.1f}亿 | " f"利润:{row['利润(亿)']:.1f}亿") def print_high_dividend_stocks(df): """打印高分红股票报告""" if not df.empty: print("\n" + "="*50) print("高分红率股票(>3%):") print("="*50) for _, row in df.iterrows(): print(f" {row['code']} {row['name']} ({row['板块']}) | " f"分红率:{row['分红率']:.2f}% | " f"市值:{row['市值(亿)']:.1f}亿") else: print("\n未找到分红率>3%的股票") def save_results(top_stocks, high_dividend_stocks): """保存结果到CSV文件""" timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") top_file = f"龙头股分析_{timestamp}.csv" div_file = f"高分红股票_{timestamp}.csv" # 优化输出格式 output_columns = ['code', 'name', '板块', '分红率', '市值(亿)', '营收(亿)', '利润(亿)', '平均分红'] top_stocks[output_columns].to_csv(top_file, index=False, float_format='%.2f') high_dividend_stocks[output_columns].to_csv(div_file, index=False, float_format='%.2f') print("\n" + "="*50) print(f"结果已保存: {top_file}, {div_file}") print("="*50) if __name__ == "__main__": main()
五:运行效果
原始运行效果:
错误较多,主要分为以下大类:
1、akshare库接口使用错误,和库变化较快以及不够大众化有关
2、本质上对于数据爬取,都会出现很多失败,需要考虑反爬
人工校验后运行效果:

六:思考
怎么提高质量?
1、提高提示词质量 --> prompt工程知识有限怎么办? --->模型对promot进行完善
2、细化需求,强调任务
开心每一天,写代码的小熊猫~