资讯动态

DeepSeek证券研报自动化:从数据到文档的工程化生成链路

发布时间:2026/9/30 4:53:15 来源:尧图企业网站定制
简介这份257页的PDF文档面向金融科技从业者、量化研究员与AI工程师系统讲解如何用DeepSeek-R1构建证券研报自动化生成方案解决人工研报撰写效率低、数据来源分散、专业术语难以统一等痛点。内容从多源异构金融数据预处理、财经文本清洗与向量化到Prompt工程、研报Schema设计、标注体系与数据集构建再到预训练语料融入、监督微调、分布式训练调度、LoRA与QLoRA低资源微调以及金融术语库与知识库融合最后落到研报生成质量的多维评估指标共48个大章节覆盖金融数据分析与投资策略生成全链路。资源包为1个PDF文件约11.71MB支持目录跳转与左侧书签大纲定位便于按章节查阅。目前已有177人学习适合希望把深度学习模型落地到证券研报场景的读者作为技术参考与方案蓝本。1. 证券研报自动化从 257 页方案到可跑通的生成链路一份 257 页的证券研报如果靠人工从 Wind 拉数据、Excel 算指标、Word 排版一个熟练分析师至少需要三到五个工作日。而 DeepSeek 证券研报自动化生成方案要解决的核心问题是把这条链路压缩到分钟级用 DeepSeek 做语义理解与文本生成用金融数据分析做因子计算与信号提取最终自动输出包含投资策略建议的完整研报。这套方案适合三类人想搭投研中台的券商 IT、需要批量覆盖中小市值标的的量化研究员、以及希望把研报生产效率提升一个数量级的独立投顾。它不是一个提示词模板而是一条从数据采集、指标计算、策略生成到文档渲染的工程流水线。下面按落地顺序拆开讲每一步都给到能直接复现的命令和参数。2. 数据层与指标层把行情、财务、舆情接进同一条管道2.1 三类数据源的接入方式与字段映射证券研报的自动化生成第一步不是调模型而是把数据接进来。常见的数据源分三类行情数据日线、分钟线、财务数据利润表、资产负债表、现金流量表、舆情数据公告、新闻、研报摘要。行情和财务数据一般通过 Tushare、AkShare 或券商自研接口获取舆情数据则需要爬取或采购。我一般会先把三类数据统一成一张宽表字段命名遵循ts_code、trade_date、report_period三个主键避免后续 join 时出现字段歧义。import akshare as ak import pandas as pd # 拉取某只股票的日线行情前复权 daily ak.stock_zh_a_hist( symbol000001, perioddaily, start_date20240101, end_date20241231, adjustqfq # 前复权避免除权除息造成指标断裂 ) daily daily.rename(columns{ 日期: trade_date, 开盘: open, 收盘: close, 最高: high, 最低: low, 成交量: vol, 成交额: amount }) daily[trade_date] pd.to_datetime(daily[trade_date]) daily daily.sort_values(trade_date).reset_index(dropTrue) print(daily[[trade_date, close, vol]].tail())这段代码的关键参数是adjustqfq。做研报时如果不用前复权遇到分红送转会导致均线、收益率等指标出现跳空生成的投资策略会基于错误的价格序列。period选daily是因为研报的策略周期通常以周或月为单位分钟线只在做日内择时的时候才需要。拉完行情后财务数据用ak.stock_financial_report_sina按报告期拉取舆情数据用ak.stock_news_em按股票代码拉取三张表通过ts_code和日期对齐。2.2 研报常用的 12 个核心指标计算数据接进来之后需要计算研报里必须出现的指标。我整理了一份最小指标集覆盖估值、盈利、成长、动量四个维度共 12 个。这些指标不是拍脑袋选的而是翻了几十份卖方研报后出现频率最高的。维度指标名计算方式参数说明估值PE_TTM收盘价 / 每股收益TTM亏损时置空不参与排序估值PB收盘价 / 每股净资产金融股需单独处理估值股息率每股分红 / 收盘价用最近12个月分红盈利ROE净利润 / 净资产用TTM口径盈利毛利率(营收-营业成本) / 营收单季度值成长营收同比(本期营收-去年同期) / 去年同期单季度同比成长净利润同比同上单季度同比动量20日涨跌幅close / close.shift(20) - 1复权后计算动量60日涨跌幅close / close.shift(60) - 1复权后计算波动20日波动率std(日收益率, 20) * sqrt(252)年化流动性20日日均成交额mean(amount, 20)单位万元质量资产负债率总负债 / 总资产最新报告期# 计算动量与波动率指标 daily[ret] daily[close].pct_change() daily[mom_20] daily[close] / daily[close].shift(20) - 1 daily[mom_60] daily[close] / daily[close].shift(60) - 1 daily[vol_20] daily[ret].rolling(20).std() * (252 ** 0.5) daily[amt_ma20] daily[amount].rolling(20).mean() # 财务指标合并按报告期对齐 fin ak.stock_financial_abstract(symbol000001) fin[report_period] pd.to_datetime(fin[报告期]) merged pd.merge_asof( daily.sort_values(trade_date), fin.sort_values(report_period), left_ontrade_date, right_onreport_period, directionbackward # 用最近一期已披露财报避免未来函数 )directionbackward是这里最关键的参数。如果用nearest或forward会把未来才披露的财报数据合并到当前交易日回测时收益率虚高实盘时策略失效。这是量化研报自动化里最常见的翻车点之一。指标算完后把宽表存成 Parquet 格式后续 DeepSeek 生成文本时直接读取避免每次重复计算。3. DeepSeek 接入与提示词工程让模型输出结构化策略3.1 DeepSeek API 的调用方式与参数配置数据准备好之后下一步是把指标喂给 DeepSeek让它生成投资策略文本。DeepSeek 提供兼容 OpenAI 格式的 API调用方式很直接。我一般用deepseek-chat模型做文本生成用deepseek-reasoner做需要多步推理的策略判断。API 调用时最关键的三个参数是temperature、max_tokens和response_format。from openai import OpenAI client OpenAI( api_keyyour_api_key, base_urlhttps://api.deepseek.com # DeepSeek 官方接口地址 ) resp client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: 你是一名卖方分析师输出结构化投资策略。}, {role: user, content: prompt} ], temperature0.3, # 研报要求稳定温度调低 max_tokens2048, response_format{type: json_object} # 强制 JSON 输出 ) strategy resp.choices[0].message.contenttemperature0.3是研报场景的甜点值。调到 0.7 以上模型会开始编造不存在的财务数据调到 0 以下输出会变得机械重复。response_format设为json_object后模型会保证输出合法 JSON后续解析不会因为格式问题报错。max_tokens设 2048 是因为一份单标的策略文本通常在 800 到 1500 字之间留出余量防止截断。3.2 把指标表转成模型能稳定解析的提示词直接把 DataFrame 转成字符串丢给模型效果往往不稳定。我一般会把指标整理成 Markdown 表格再配上明确的输出格式要求。提示词分三段角色设定、数据输入、输出约束。prompt f 你是一名有十年经验的卖方分析师。根据以下指标生成投资策略。 ## 指标数据 | 指标 | 数值 | |------|------| | PE_TTM | {pe:.2f} | | ROE | {roe:.2%} | | 营收同比 | {rev_yoy:.2%} | | 20日涨跌幅 | {mom_20:.2%} | | 20日波动率 | {vol_20:.2%} | ## 输出要求 1. 用 JSON 输出字段包括rating买入/增持/中性/减持、target_price、logic200字以内、risks数组3条 2. rating 必须基于指标给出不得编造数据 3. target_price 用 PE 估值法给出计算过程 输出约束里最重要的是「不得编造数据」和「给出计算过程」。前者防止模型幻觉后者让策略可追溯。实际跑下来加了这两条之后策略文本的可用率从 60% 提升到 90% 以上。解析 JSON 时用json.loads如果失败就重试一次重试时把 temperature 降到 0.1。4. 策略生成与文档渲染从 JSON 到可交付的研报4.1 投资策略的自动生成逻辑与评级映射DeepSeek 返回的 JSON 里rating字段是核心。但模型给的评级有时和指标矛盾比如 ROE 为负却给「买入」。我一般会在模型输出后加一层规则校验用指标阈值做兜底。def validate_rating(strategy: dict, metrics: dict) - str: rating strategy[rating] roe metrics[roe] mom metrics[mom_20] # 规则兜底ROE为负且动量向下强制降级 if roe 0 and mom -0.1: return 减持 # ROE大于15%且动量向上允许买入 if roe 0.15 and mom 0.05 and rating 买入: return 买入 return rating这层校验的意义在于模型是概率生成规则是确定性判断。两者结合既保留了模型的文本组织能力又用规则守住了评级的底线。target_price的计算用 PE 估值法取行业平均 PE 乘以每股收益再打八折作为安全边际。行业平均 PE 从申万行业分类里取这部分数据需要单独维护一张行业估值表。4.2 用模板引擎渲染最终研报文档策略 JSON 生成后最后一步是渲染成 Word 或 PDF。我一般用python-docx做 Word 渲染用jinja2做模板管理。模板里预留占位符渲染时把 JSON 字段填进去。from docx import Document from docx.shared import Pt doc Document() doc.add_heading(f{stock_name} 投资策略报告, level1) doc.add_paragraph(f评级{strategy[rating]}) doc.add_paragraph(f目标价{strategy[target_price]}) doc.add_heading(核心逻辑, level2) doc.add_paragraph(strategy[logic]) doc.add_heading(风险提示, level2) for risk in strategy[risks]: doc.add_paragraph(risk, styleList Bullet) doc.save(f{ts_code}_研报.docx)渲染时注意两点一是中文字体要显式设置否则 Word 里会显示成方框二是表格里的数字要格式化PE 保留两位小数百分比保留一位小数。这两点不做研报看起来就不像人写的。渲染完成后可以再用docx2pdf转成 PDF方便分发。5. 避坑与排查自动化研报生成里最容易翻车的 5 个点5.1 财报数据用了未来函数现象回测时策略收益率很高实盘跑起来完全不是那么回事。原因合并财务数据时用了directionforward或nearest把未来才披露的财报合并到了当前交易日。解决一律用directionbackward并且加一列ann_date公告日期确保合并时用的是公告日而非报告期。5.2 DeepSeek 输出 JSON 解析失败现象json.loads报错提示Expecting value。原因模型在 JSON 前后加了说明文字或者用了单引号。解决调用时设response_format{type: json_object}解析前先用正则提取{...}部分失败则降 temperature 重试一次。5.3 指标计算时除权除息没处理现象均线在分红日出现断崖动量指标失真。原因用了不复权或后复权价格。解决行情数据统一用前复权adjustqfq财务数据用最新报告期两者对齐时注意日期口径。5.4 模型编造不存在的财务数据现象策略文本里出现「公司 2024 年营收 500 亿」但实际只有 50 亿。原因提示词里没有明确禁止编造且 temperature 偏高。解决提示词加「不得编造数据所有数字必须来自输入指标」temperature 降到 0.3 以下输出后加一层数字校验。5.5 渲染出的 Word 中文显示为方框现象生成的研报打开后中文全是方框。原因python-docx默认字体不支持中文。解决在add_paragraph后显式设置run.font.name SimSun并设置run._element.rPr.rFonts.set(qn(w:eastAsia), SimSun)。6. 进阶技巧用批量并发把单篇研报压到 30 秒内单只股票的研报生成跑通后下一步是批量覆盖。如果串行跑100 只股票按每只 20 秒算需要 33 分钟。用并发可以压到 30 秒以内。我一般用concurrent.futures做线程池把数据拉取、指标计算、模型调用、文档渲染四个阶段拆开每个阶段独立并发。from concurrent.futures import ThreadPoolExecutor, as_completed def generate_one(ts_code): daily fetch_daily(ts_code) metrics calc_metrics(daily) strategy call_deepseek(metrics) render_doc(ts_code, strategy) return ts_code, ok with ThreadPoolExecutor(max_workers8) as executor: futures {executor.submit(generate_one, code): code for code in stock_list} for future in as_completed(futures): code, status future.result() print(f{code}: {status})max_workers8是经验值。DeepSeek API 有并发限制设太高会触发限流设太低又压不住时间。8 个线程在大多数账号等级下能稳定跑。如果遇到429错误把 workers 降到 4并在调用处加time.sleep(1)做退避。另外数据拉取阶段建议加本地缓存同一只股票当天第二次拉取直接读 Parquet不重复请求接口。验证批量结果时我习惯抽三只股票人工核对一只大盘股、一只小盘股、一只亏损股。大盘股看指标是否合理小盘股看流动性指标是否缺失亏损股看评级是否被规则兜底降级。这三只过了整批基本没问题。最后说个血泪经验别在周五下午跑批量接口经常维护跑一半失败还得重来。我一般选周二到周四上午跑成功率最高。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑