基于Python与GPT的自动化股票报告生成系统实践
1. 项目概述从零构建一个AI驱动的自动化股票报告生成器最近在捣鼓一个挺有意思的小项目我把它叫做“AI股票报告生成器”。核心想法很简单能不能让程序自动去抓取我关心的股票数据然后扔给类似ChatGPT这样的AI模型让它帮我写一份结构清晰、有数据支撑的每日或每周简报这样我就不用每天开盘前手忙脚乱地去各个财经网站扒数据、自己组织语言写分析了。对于我这种既要盯盘又懒得写日报的散户来说如果能有个自动化工具每天早上准时把报告推送到微信或者邮箱那体验就太棒了。这个项目的技术栈并不复杂但把各个环节串联起来形成一个稳定、可靠的服务里面有不少细节需要打磨。整个流程可以拆解为几个核心环节数据获取、数据处理、AI报告生成、结果交付。数据获取是基础你得有可靠、及时的数据源数据处理是关键原始数据往往是杂乱无章的需要清洗、计算成AI能理解的格式AI报告生成是核心如何设计提示词让AI写出专业、客观的报告而不是一本正经地胡说八道最后生成的报告得用一种舒服的方式送到我面前。下面我就把这几个月折腾这个项目的思路、踩过的坑以及最终的实现方案详细跟大家分享一下。2. 核心思路与架构设计2.1 为什么选择“数据AI”的自动化方案手动分析股票费时费力而且容易受情绪影响。市面上已有的股票软件分析报告要么太泛泛要么是收费服务且不一定符合我的个人关注点。我的需求很明确个性化、自动化、低成本。个性化是指报告只关注我自选股池里的股票自动化是指整个流程无需人工干预低成本是指尽量利用免费或低成本的资源。“数据AI”的组合完美契合了这些需求。数据部分有很多免费的金融数据API如新浪、腾讯、雅虎财经的公开接口可以获取实时或历史的行情、财务数据。AI部分以OpenAI的GPT系列为代表的语言模型在理解和生成结构化文本方面已经非常强大。我的角色就从“数据分析师报告撰写员”转变为“系统架构师提示词工程师”一次性投入时间搭建好管道之后就可以享受每天自动产出的成果。2.2 系统架构总览我设计的系统架构遵循了清晰的分层和模块化思想这样便于维护和扩展。整个系统可以看作一个数据流水线Data Pipeline数据采集层负责从多个源头如公开API、财经网站定时抓取预设股票列表的基础数据价格、成交量、涨跌幅、市盈率等。数据处理与增强层对原始数据进行清洗、校验并计算一些常用的技术指标如移动平均线MA、相对强弱指数RSI、布林带等为AI分析提供更丰富的输入。AI报告生成层这是核心。将处理好的数据结合精心设计的提示词Prompt发送给AI模型如GPT-3.5/4请求其生成包含概述、技术面分析、基本面亮点、风险提示等部分的报告。输出与交付层将AI生成的纯文本报告格式化为更易读的Markdown或HTML然后通过预设的渠道如电子邮件、企业微信机器人、Telegram Bot推送给我。整个流程由定时任务例如Linux的Cron或Python的APScheduler驱动比如设定在每个交易日晚上8点自动执行一次生成当日的复盘报告。注意金融数据具有时效性和准确性要求。免费公开接口可能存在延迟、偶尔失效或数据格式变动的情况。在生产环境中使用务必做好错误处理、日志记录和数据校验并考虑备用数据源。2.3 技术选型考量编程语言Python是自然之选。它在数据抓取Requests, Scrapy、数据分析Pandas, NumPy、与AI API交互方面有极其丰富的库生态开发效率高。数据获取初期可以使用yfinance(雅虎财经) 或akshare(一个强大的国内财经数据库) 这类封装好的库。它们免费、易用但需要了解其限制和稳定性。AI模型OpenAI API是目前最成熟的选择。GPT-3.5-turbo在成本和质量上取得了很好的平衡适合此类文本生成任务。也可以探索国内大模型厂商的API但需要注意其对金融内容生成的合规性限制。任务调度对于简单的每日任务服务器上的Cron是最简单稳定的方案。如果调度逻辑复杂如避开非交易日可以在Python脚本内使用APScheduler库。部署可以运行在一台始终在线的云服务器如最低配的Linux VPS上或者利用GitHub Actions的定时任务功能实现“无服务器”运行后者更省心且免费额度通常够用。3. 核心模块实现细节3.1 数据获取与处理模块数据是分析的基石。我首先需要构建一个可靠的数据获取模块。1. 股票列表管理我创建了一个配置文件如stocks.yaml或stocks.json来管理我关注的股票。这样增删股票只需修改配置文件无需改动代码。# config/stocks.yaml watch_list: - code: “000001.SZ” # 平安银行A股 name: “平安银行” market: “SZ” - code: “AAPL” # 苹果公司美股 name: “Apple Inc.” market: “US” - code: “00700” # 腾讯控股港股使用akshare时需要 name: “腾讯控股” market: “HK”2. 数据抓取实现以使用akshare获取A股数据为例。我们需要获取当日行情和部分历史数据用于计算指标。import akshare as ak import pandas as pd from datetime import datetime, timedelta def fetch_stock_data(stock_code, stock_name, market): 获取单只股票的数据 stock_info {} try: # 1. 获取实时行情数据 if market “SZ” or market “SH”: # akshare获取A股实时行情 df_real ak.stock_zh_a_spot_em() # 从返回的DataFrame中筛选出目标股票 target_stock df_real[df_real[“代码”] stock_code].iloc[0] stock_info[“current_price”] target_stock[“最新价”] stock_info[“change”] target_stock[“涨跌额”] stock_info[“change_percent”] target_stock[“涨跌幅”] stock_info[“volume”] target_stock[“成交量”] stock_info[“amount”] target_stock[“成交额”] # 2. 获取近期历史数据用于计算指标 end_date datetime.now().strftime(“%Y%m%d”) start_date (datetime.now() - timedelta(days60)).strftime(“%Y%m%d”) # 取最近60天 if market in [“SZ”, “SH”]: df_hist ak.stock_zh_a_hist(symbolstock_code, period“daily”, start_datestart_date, end_dateend_date, adjust“qfq”) # 计算简单移动平均线MA20 if not df_hist.empty: df_hist[‘MA20’] df_hist[‘收盘’].rolling(window20).mean() stock_info[‘latest_MA20’] df_hist[‘MA20’].iloc[-1] stock_info[‘latest_close’] df_hist[‘收盘’].iloc[-1] except Exception as e: print(f“获取股票 {stock_code} 数据失败: {e}”) stock_info[“error”] str(e) return stock_info3. 数据清洗与指标计算抓取到的数据需要清洗处理缺失值、异常值并计算技术指标。这里以计算RSI相对强弱指数为例这是一个常用的动量指标。def calculate_rsi(prices, period14): 计算RSI指标 deltas np.diff(prices) seed deltas[:period1] up seed[seed 0].sum()/period down -seed[seed 0].sum()/period rs up/down if down ! 0 else 0 rsi np.zeros_like(prices) rsi[:period] 100. - 100./(1.rs) for i in range(period, len(prices)): delta deltas[i-1] if delta 0: upval delta downval 0. else: upval 0. downval -delta up (up*(period-1) upval)/period down (down*(period-1) downval)/period rs up/down if down ! 0 else 0 rsi[i] 100. - 100./(1.rs) return rsi # 在数据处理流程中调用 df_hist[‘RSI_14’] calculate_rsi(df_hist[‘收盘’].values) stock_info[‘latest_RSI’] df_hist[‘RSI_14’].iloc[-1]实操心得免费数据源可能不稳定。我的策略是“重试降级”。如果首选接口失败自动切换到备用接口例如akshare失败后尝试用yfinance抓取同一只股票的港股或美股数据。同时将所有抓取到的原始数据立即保存到本地CSV或数据库即使后续分析出错也有原始记录可追溯。3.2 AI报告生成模块提示词工程是关键这是项目的“大脑”。如何让AI写出专业、有用且不胡编乱造的报告完全取决于你给它的“指令”——也就是提示词。1. 构建系统化的提示词Prompt我的提示词通常包含以下几个部分角色设定明确告诉AI它扮演的角色例如“你是一位经验丰富、风格稳健的股票市场分析师”。任务指令清晰说明需要它做什么例如“请根据以下提供的股票数据生成一份简洁的每日分析报告”。输入数据格式化将之前处理好的数据以清晰、结构化的文本形式提供给AI。例如股票名称平安银行 (000001.SZ) 当前价12.34元 今日涨跌0.56元 (4.76%) 成交量1.23亿股 20日均线(MA20)11.89元 相对强弱指数(RSI_14)68.5 当前价高于MA20RSI接近超买区间70输出格式要求严格规定报告的结构。这能确保每次生成的报告风格统一便于阅读。报告请严格按以下结构组织 1. 今日概览用一两句话总结该股票今日的整体表现。 2. 技术面观察结合提供的价格、MA20、RSI等数据分析短期走势和关键位置。 3. 简要点评基于上述分析给出中性、谨慎乐观或需要注意风险等定性看法。**注意不得给出具体的投资建议如“买入”、“卖出”。** 4. 明日关注列出1-2个需要关注的点如“能否站稳MA20上方”、“成交量是否持续放大”。风格与限制规定语言风格如“专业、简洁、客观”并加入重要限制例如“所有分析必须严格基于提供的数据不要编造数据或信息”。2. 与OpenAI API交互构建好提示词后通过API调用模型。import openai from tenacity import retry, stop_after_attempt, wait_exponential openai.api_key “YOUR_API_KEY” retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def generate_stock_report_with_ai(stock_data_dict): 调用AI生成单只股票报告 # 1. 构建用户消息即我们的提示词数据 user_content f“”” 你是一位经验丰富、风格稳健的股票市场分析师。 请根据以下提供的股票数据生成一份简洁的每日分析报告。 【股票数据】 股票名称{stock_data_dict[‘name’]} ({stock_data_dict[‘code’]}) 当前价{stock_data_dict[‘current_price’]}元 今日涨跌{stock_data_dict[‘change’]}元 ({stock_data_dict[‘change_percent’]}%) 成交量{stock_data_dict[‘volume’]}股 20日均线(MA20){stock_data_dict.get(‘latest_MA20’, ‘N/A’)}元 相对强弱指数(RSI_14){stock_data_dict.get(‘latest_RSI’, ‘N/A’)} 补充描述{stock_data_dict.get(‘technical_note’, ‘’)} 报告请严格按以下结构组织 1. 今日概览用一两句话总结该股票今日的整体表现。 2. 技术面观察结合提供的价格、MA20、RSI等数据分析短期走势和关键位置。 3. 简要点评基于上述分析给出中性、谨慎乐观或需要注意风险等定性看法。**注意不得给出具体的投资建议如“买入”、“卖出”。** 4. 明日关注列出1-2个需要关注的点。 请使用专业、简洁、客观的语言。所有分析必须严格基于提供的数据。 “”” # 2. 调用ChatCompletion API response openai.ChatCompletion.create( model“gpt-3.5-turbo”, # 根据成本和需求选择模型 messages[ {“role”: “system”, “content”: “你是一个专业的金融分析助手。”}, {“role”: “user”, “content”: user_content} ], temperature0.3, # 温度设低一些让输出更稳定、更专注于事实 max_tokens800, # 控制报告长度 ) # 3. 提取并返回生成的报告内容 report_content response.choices[0].message.content return report_content注意事项API调用有成本并且可能失败。务必使用tenacity等库实现重试机制。temperature参数很关键在分析类任务中建议设置在0.1-0.5之间以获得更确定、更少“创造性”的输出。务必在提示词中强调“基于提供的数据”并明确禁止其给出具体投资建议这是重要的合规和安全措施。3.3 报告格式化与推送模块AI生成的报告是纯文本我们可以将其美化并推送出去。1. 报告格式化将多只股票的独立报告汇总并格式化为一个完整的日报。使用Markdown可以很容易地转换为HTML或在支持Markdown的平台上获得良好渲染。def generate_daily_summary_report(stock_reports_dict): 生成每日总结报告 report_date datetime.now().strftime(“%Y年%m月%d日”) markdown_content f“# 股票市场每日简报\n\n**报告日期{report_date}**\n\n---\n\n” for stock_code, report in stock_reports_dict.items(): markdown_content f“## {report[‘name’]} ({stock_code})\n\n” markdown_content f“**收盘价:** {report[‘data’].get(‘current_price’, ‘N/A’)} | ” markdown_content f“**涨跌幅:** {report[‘data’].get(‘change_percent’, ‘N/A’)}%\n\n” markdown_content f“{report[‘ai_content’]}\n\n---\n\n” markdown_content “*本报告由AI自动生成数据来源于公开市场仅供参考不构成任何投资建议。*\n” return markdown_content2. 推送渠道集成电子邮件使用smtplib和email库。可以将Markdown内容作为HTML邮件正文发送。import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart def send_email_via_smtp(subject, html_content, to_emails): msg MIMEMultipart(‘alternative’) msg[‘Subject’] subject msg[‘From’] smtp_username msg[‘To’] ‘, ‘.join(to_emails) # 添加HTML版本 html_part MIMEText(html_content, ‘html’) msg.attach(html_part) with smtplib.SMTP_SSL(smtp_server, smtp_port) as server: server.login(smtp_username, smtp_password) server.send_message(msg)企业微信/钉钉机器人这是更即时、更方便的方式。只需向机器人提供的Webhook地址发送一个HTTP POST请求即可。import requests import json def send_to_wechat_work_bot(markdown_content, webhook_url): data { “msgtype”: “markdown”, “markdown”: { “content”: markdown_content } } headers {‘Content-Type’: ‘application/json’} response requests.post(webhook_url, datajson.dumps(data), headersheaders) return response.status_code 2004. 系统集成、调度与部署4.1 编写主执行脚本将上述所有模块整合到一个主脚本中。# main.py import yaml from datetime import datetime import logging # 导入自定义模块 from data_fetcher import fetch_all_stocks_data from ai_analyzer import generate_report_for_stocks from reporter import generate_daily_summary_report, send_to_wechat_work_bot def main(): logging.basicConfig(levellogging.INFO, format‘%(asctime)s - %(levelname)s - %(message)s’) logger logging.getLogger(__name__) logger.info(“开始执行每日股票报告生成任务...”) # 1. 加载配置 with open(‘config/stocks.yaml’, ‘r’, encoding‘utf-8’) as f: config yaml.safe_load(f) watch_list config[‘watch_list’] # 2. 获取数据 logger.info(“正在获取股票数据...”) all_stocks_data {} for stock in watch_list: data fetch_stock_data(stock[‘code’], stock[‘name’], stock[‘market’]) if data and ‘error’ not in data: all_stocks_data[stock[‘code’]] { ‘name’: stock[‘name’], ‘data’: data } else: logger.error(f“股票 {stock[‘code’]} 数据获取失败: {data.get(‘error’, ‘Unknown’)}”) if not all_stocks_data: logger.error(“未获取到任何有效股票数据任务终止。”) return # 3. AI分析并生成报告 logger.info(“正在调用AI生成分析报告...”) stock_reports_with_ai generate_report_for_stocks(all_stocks_data) # 4. 生成总结报告 logger.info(“正在生成每日总结报告...”) daily_markdown_report generate_daily_summary_report(stock_reports_with_ai) # 5. 推送报告 logger.info(“正在推送报告...”) webhook_url “YOUR_WEBHOOK_URL” if send_to_wechat_work_bot(daily_markdown_report, webhook_url): logger.info(“报告推送成功”) else: logger.error(“报告推送失败”) # 6. (可选) 将报告保存到本地文件用于存档 report_date datetime.now().strftime(“%Y%m%d”) with open(f“reports/daily_report_{report_date}.md”, ‘w’, encoding‘utf-8’) as f: f.write(daily_markdown_report) logger.info(“每日股票报告生成任务执行完毕。”) if __name__ “__main__”: main()4.2 任务调度Linux Cron (推荐)在服务器上这是最经典可靠的方式。# 编辑crontab crontab -e # 添加一行在每个工作日晚上8点执行假设服务器时间已校准 0 20 * * 1-5 cd /path/to/your/project /usr/bin/python3 main.py /path/to/logs/stock_report.log 21GitHub Actions (无服务器方案)如果你的代码托管在GitHub可以利用其定时任务。# .github/workflows/daily-stock-report.yml name: Daily Stock Report on: schedule: - cron: ‘0 12 * * 1-5’ # UTC时间每天12点即北京时间20点周一到周五运行 workflow_dispatch: # 允许手动触发 jobs: generate-report: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: ‘3.9’ - name: Install dependencies run: pip install -r requirements.txt - name: Run report generator env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} WECHAT_WEBHOOK: ${{ secrets.WECHAT_WEBHOOK }} run: python main.py注意需要在GitHub仓库的Settings - Secrets中配置你的API密钥和Webhook地址。4.3 环境配置与依赖管理创建一个requirements.txt文件管理Python依赖。# requirements.txt openai1.0.0 akshare1.10.0 pandas1.5.0 numpy1.23.0 requests2.28.0 tenacity8.2.0 PyYAML6.0使用虚拟环境隔离项目依赖是一个好习惯python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows pip install -r requirements.txt5. 常见问题、优化与进阶思考5.1 实际运行中遇到的典型问题数据源不稳定或变更这是最常见的问题。某天akshare的接口突然返回空数据或格式变了。排查首先查看脚本日志确认错误信息。然后手动在Python交互环境或浏览器中测试对应的数据接口。解决立即更新akshare库到最新版本pip install -U akshare。如果问题依旧考虑在代码中为该数据源添加备用方案或暂时注释掉该股票的分析。AI生成内容“胡言乱语”或格式错误有时AI会忽略指令不按指定格式输出或者加入一些它自己“想象”的数据。排查检查发送给API的完整提示词内容确认数据部分和指令部分是否清晰无误。查看API返回的完整响应。解决强化提示词中的限制性语句如“必须严格基于以下数据”、“请严格按照以下四点结构输出”。降低temperature参数值如从0.7降到0.3。在代码端增加对输出格式的简单校验如果发现没有按章节输出可以记录错误或尝试重新生成。API调用超时或达到速率限制当股票列表较长时连续调用AI API可能触发速率限制。排查OpenAI API会返回明确的错误码如429。解决在调用函数中加入指数退避的重试机制如上文代码中的retry装饰器。在批量处理多只股票时在请求之间加入短暂的延时如time.sleep(1)。推送失败邮件发送被拒或企业微信Webhook地址失效。排查检查SMTP服务器设置、用户名密码是否正确。检查企业微信机器人是否被禁用。解决确保使用授权码而非邮箱密码登录SMTP。对于关键通知可以考虑实现“失败-重试”逻辑或者添加一个备用的推送渠道如同时发送邮件和微信消息。5.2 项目优化方向数据丰富化引入更多指标除了MA、RSI可以加入MACD、布林带、成交量加权平均价(VWAP)等。整合基本面数据尝试抓取市盈率(PE)、市净率(PB)、每股收益(EPS)等让AI的分析维度更全面。关联市场情绪获取相关新闻标题或摘要需注意信息来源的可靠性让AI结合舆情进行分析。提示词精细化分角色提示可以设计不同的“分析师角色”如“技术派分析师”、“价值投资者”让AI从不同视角生成报告提供更立体的观点。迭代优化将AI生成的报告与你自己的手动分析对比找出AI的不足不断调整和优化提示词这是一个持续的过程。系统健壮性提升完善监控与告警除了记录日志可以增加关键环节如数据获取失败、API调用异常的即时告警通过另一个独立的通知渠道如短信发送给你。数据持久化将每日获取的原始数据和分析结果存入SQLite或MySQL数据库便于后续进行回测和长期趋势分析。报告形式多样化可视化图表使用matplotlib或plotly生成股价走势图、指标叠加图将图片插入报告更加直观。语音播报利用TTS文本转语音技术将报告摘要生成语音文件在通勤路上收听。5.3 关于合规性与风险的思考这是一个必须严肃对待的问题。本项目生成的报告绝对不可视为投资建议它只是一个信息整理和初步分析的自动化工具。在提示词和最终报告中明确免责声明这是底线。必须在AI的指令和最终报告末尾强调“本报告由AI自动生成数据来源于公开市场仅供参考不构成任何投资建议”。保持分析的客观性提示词应引导AI进行描述性、分析性陈述避免使用任何具有明确导向性的词汇如“强烈推荐”、“必须卖出”。数据源的可靠性对使用的免费数据源要有清醒认识知晓其可能存在的延迟和误差。对于关键决策仍需以官方或付费的权威数据为准。个人使用为主这个项目非常适合个人投资者作为辅助工具。如果考虑分享或扩大使用范围务必咨询相关法律人士了解在金融信息传播方面的合规要求。搭建这个自动化股票报告生成器的过程更像是一次有趣的工程实践。它让我把数据抓取、处理、AI应用和系统部署这几个环节串了起来。最大的收获不是省下了每天看盘的那点时间而是建立了一套属于自己的、可定制、可迭代的信息处理流程。现在每天早上打开手机就能看到一份为我量身定制的市场简报那种感觉确实很踏实。如果你也对Python和AI感兴趣并且想为自己的投资找一个“数字助理”不妨从关注三五只股票开始亲手搭建一个试试看过程中遇到的具体问题才是学习最快的地方。