☰
大模型Agent金融信号生成与夏普比率组合优化实战
2026/10/3 4:39:28 网站建设 项目流程

1. 从一条标题拆出来的真实需求:大模型荐股到底靠不靠谱

第一次看到“ChatGPT大模型荐股与夏普比率投资组合优化系统”这个标题,我脑子里冒出来的第一个念头不是“这玩意儿能赚钱吗”,而是“这套东西到底怎么把自然语言模型和量化金融的数学框架缝在一起”。因为但凡做过一点量化的人都知道,荐股和组合优化是两个完全不同的工种——前者偏主观判断和信号生成,后者是纯粹的数学规划问题。把大模型塞进这个链路里,它到底扮演什么角色,这才是整个项目最值得聊的地方。

先把话说在前头:这篇文章不构成任何投资建议,所有涉及标的、参数、代码的内容都只作为技术实现的示例。我做这个项目的出发点很简单,就是想验证一件事——大模型在金融信号生成这个环节,能不能比传统的因子模型或者技术指标提供一些增量信息。结论我先不急着给,咱们一步步拆。

这套系统适合谁看?如果你是有一定Python基础、对量化交易有初步了解、想看看大模型怎么落地到金融场景的开发者,那这篇内容会对你有直接帮助。如果你是完全零基础的小白,也不用慌,我会把每个环节的原理和操作都讲清楚,你跟着走一遍也能跑通。整个系统涉及的核心技术栈包括:Python数据处理、大模型API调用、Agent任务编排、夏普比率计算、组合优化求解。听起来吓人,但拆开来看每一块都不复杂。

我先把整个系统的骨架说清楚,让你有个全局认知。系统分四层:第一层是数据层,负责拉取行情数据、财务数据和新闻文本;第二层是信号层,用大模型Agent对新闻和公告做情感分析、事件抽取,生成看多看空信号;第三层是组合层,把信号转化成预期收益向量,结合协方差矩阵做均值方差优化,目标是最大化夏普比率;第四层是回测层,用历史数据验证策略表现,输出净值曲线和风险指标。这四层里,大模型只在第二层发挥作用,其他三层都是传统量化方法。这个设计很关键,后面我会详细解释为什么不能让大模型碰组合优化。

2. 系统整体架构设计与技术选型逻辑

2.1 为什么用Agent而不是直接调大模型API

很多人做类似项目的第一反应是:写个prompt,把新闻丢给大模型,让它输出“买入/卖出/持有”,完事。我一开始也这么干过,实测下来问题很大。单次调用的输出极不稳定,同一篇新闻你换个时间问,它可能给你完全相反的结论。而且它没法处理多步推理——比如一条新闻里提到某公司拿到了大订单,但同时又提到原材料涨价可能压缩利润,这种矛盾信息单次调用很难权衡。

Agent的价值就在这里。我设计了一个三层Agent结构:第一层是信息抽取Agent,负责从原始文本里提取结构化事件,比如“公司A获得政府补贴5000万”;第二层是影响评估Agent,判断这个事件对哪些行业、哪些公司有正向或负向影响,影响力度大概多大;第三层是信号聚合Agent,把多个事件的影响汇总成一个综合评分。每一层Agent都有明确的输入输出格式,层与层之间通过结构化数据传递,而不是自然语言。这样做的好处是每一步都可追溯、可调试,出了问题你知道是哪一层判断错了。

Agent的编排我用的是轻量级的方案,没有上LangChain那种重框架。原因很简单:这个系统的Agent逻辑是固定的流水线,不需要动态规划或者工具调用,用LangChain反而增加调试成本。我直接用Python的类和方法把三个Agent串起来,每个Agent内部封装prompt模板和API调用逻辑,外部通过一个调度器按顺序执行。代码量不大,但可控性极强。

2.2 夏普比率为什么是组合优化的核心目标

夏普比率的公式大家都知道:(组合预期收益 - 无风险利率) / 组合波动率。但很多人做优化的时候只盯着收益,忽略了波动率,结果组合净值曲线跟过山车一样。夏普比率的本质是衡量“每承担一单位风险,你能拿到多少超额收益”。这个指标高,说明你的收益不是靠赌出来的,而是有风险调整后的真实超额。

在均值方差优化框架里,最大化夏普比率等价于找到切点组合——也就是从无风险利率出发,与有效前沿相切的那个点。数学上可以转化为一个二次规划问题求解。我用的是cvxpy这个库,它能把优化问题用接近数学公式的方式写出来,求解器自动处理。相比scipy.optimize,cvxpy在处理约束条件时更直观,比如“单只股票权重不超过10%”“行业暴露不超过30%”这种约束,写起来很自然。

这里有个关键细节:预期收益向量怎么来。传统方法用历史收益率均值或者因子模型预测,我用的是大模型信号加上历史动量做加权。具体来说,大模型对每只股票给出一个[-1, 1]的情感评分,我把这个评分映射成预期收益的调整项,叠加到历史收益率均值上。权重方面,大模型信号的权重设了0.3,历史动量的权重是0.7。这个比例不是拍脑袋定的,后面回测部分我会讲怎么调。

2.3 数据管道的设计:行情、财务、文本三路并行

数据层我分了三路。行情数据用akshare拉取,这个库免费且接口稳定,日线数据足够用。财务数据也是akshare,主要用市盈率、市净率、ROE这几个指标做股票池的初筛。文本数据是重点,我爬了三个来源:财经新闻网站、公司公告、社交媒体讨论。爬虫用requests加BeautifulSoup,没有上scrapy,因为数据量不大,每天几百条文本,简单脚本就够了。

文本数据进来之后要先做清洗。去重、去广告、去HTML标签这些是基本操作。然后做分词和关键词提取,我用的是jieba分词,配合自定义的金融词典。这一步的目的是把长文本压缩成关键信息,减少大模型API的token消耗。实测下来,一篇800字的新闻经过关键词提取后,压缩到200字左右,信息损失很小,但API成本降了70%。

数据存储用的是SQLite,没上MySQL或者PostgreSQL。原因很简单:这个系统的数据量是单机级别的,SQLite完全够用,而且零配置,部署方便。表结构设计了三张主表:price_daily存行情,financial_quarterly存财务,text_raw存原始文本,text_processed存处理后的文本和Agent输出。索引建在股票代码和日期上,查询速度很快。

3. 大模型Agent信号生成的核心细节与实操要点

3.1 Prompt模板的设计:结构化输出是底线

Agent能不能稳定输出,90%取决于prompt设计。我踩过的最大坑就是让大模型自由发挥,结果它今天输出“强烈推荐”,明天输出“建议关注”,格式完全不统一,后面根本没法解析。后来我强制要求输出JSON格式,并且在prompt里给出完整的schema示例。

信息抽取Agent的prompt大概长这样:

EXTRACT_PROMPT = """ 你是一个金融信息抽取助手。请从以下文本中提取所有与上市公司相关的事件。 输出格式必须为JSON数组,每个元素包含以下字段: - stock_code: 股票代码,6位数字 - stock_name: 股票名称 - event_type: 事件类型,只能是[业绩预告, 重大合同, 政策影响, 高管变动, 行业新闻]之一 - sentiment: 情感倾向,-1到1之间的浮点数,-1表示极度利空,1表示极度利好 - confidence: 置信度,0到1之间的浮点数 - reason: 判断理由,不超过50字 文本内容: {text} """

这个模板的关键在于:字段类型明确、取值范围明确、枚举值明确。大模型看到这种约束,输出稳定性会大幅提升。但即便如此,我还是加了一层校验——用jsonschema库对输出做格式验证,不合格的就重试,重试三次还不行就丢弃这条数据。实测下来,第一次调用合格率大概85%,重试一次后能到95%以上。

3.2 影响评估Agent的推理链设计

信息抽取只是第一步,真正难的是判断一个事件对股价的影响。比如“某公司获得政府补贴”,这个事件对该公司是利好,但对同行业其他公司可能是利空——因为补贴意味着政策倾斜,资源向头部集中。这种间接影响,单靠信息抽取Agent是看不出来的。

影响评估Agent的prompt里,我加入了一个推理链的引导:

IMPACT_PROMPT = """ 你是一个资深行业分析师。已知以下事件: {event_json} 请按以下步骤推理: 1. 该事件直接影响哪些公司?影响方向是什么? 2. 该事件是否会影响上下游产业链?具体影响哪些环节? 3. 该事件是否会影响同行业竞争对手?影响方向是什么? 4. 综合以上分析,给出受影响股票列表及各自的评分。 输出JSON格式,包含affected_stocks数组,每个元素有stock_code、impact_score(-1到1)、impact_reason。 """

这个推理链的设计参考了人类分析师的思考过程。实测下来,加了推理链之后,Agent对间接影响的识别率明显提升。我做过一个对比测试:100条新闻,不加推理链的Agent只识别出32条有间接影响,加了推理链之后识别出67条。当然,这里面有误报,但整体召回率上去了,对后续组合优化是有利的。

3.3 信号聚合与去噪:别让大模型带偏节奏

三个Agent跑完,你会得到一堆事件和评分。但这些评分不能直接拿来用,因为存在几个问题:第一,同一事件可能被多个Agent重复识别,导致重复计算;第二,不同事件的时间衰减不同,三天前的新闻和今天的新闻,影响力度肯定不一样;第三,大模型有时候会过度反应,把一个小事件评成极端值。

我的处理方案是三层去噪。第一层做去重,用事件类型加股票代码加日期做唯一键,重复的取置信度最高的那条。第二层做时间衰减,衰减因子用指数衰减,半衰期设了3天。也就是说,今天的新闻权重是1,三天前的权重是0.5,一周前的权重是0.25。第三层做截断,把所有评分限制在[-0.8, 0.8]之间,防止极端值主导组合。这个截断阈值是回测调出来的,后面会讲。

聚合的时候,我对每只股票把所有相关事件的评分加权求和,权重是置信度乘以时间衰减因子。最后再做一个横截面标准化,把评分映射到标准正态分布,这样不同股票之间的评分才有可比性。

注意:大模型信号只能作为辅助,不能完全替代传统因子。我在回测中发现,纯靠大模型信号的组合,夏普比率只有0.8左右,而加入动量因子后能到1.5以上。大模型的价值在于捕捉传统因子覆盖不到的事件驱动机会,而不是全面替代。

4. 组合优化与夏普比率最大化的完整实现

4.1 预期收益向量的构建:信号与动量的融合

预期收益向量是整个优化问题的输入,它的质量直接决定组合表现。我的构建方法是:对每只股票,取过去60个交易日的日收益率均值,年化后作为基础预期收益。然后加上大模型信号的调整项,调整幅度是信号评分乘以一个系数。这个系数我设的是0.02,也就是说,信号评分为1的股票,预期收益上调2%;评分为-1的,下调2%。

为什么是0.02?这个数字是回测调出来的。我试过0.01、0.02、0.05、0.1四个档位,0.02的夏普比率最高。0.01的时候信号影响太弱,跟纯动量策略差不多;0.05和0.1的时候信号影响太强,组合波动率飙升,夏普比率反而下降。这个参数跟市场环境有关,牛市里可以适当调高,熊市里要调低。

协方差矩阵的估计用的是Ledoit-Wolf收缩估计,没有直接用样本协方差。原因很简单:股票数量多的时候,样本协方差矩阵不稳定,容易导致优化结果极端化。Ledoit-Wolf方法通过向对角矩阵收缩,降低了估计误差。sklearn.covariance里有现成的实现,一行代码就能调用。

4.2 约束条件的设置:别让优化器放飞自我

无约束的均值方差优化有个臭名昭著的问题:它会给你一堆极端权重,比如某只股票权重90%,其他股票加起来10%。这种组合在实际交易中根本没法执行,而且风险极大。所以必须加约束。

我设了四类约束。第一类是权重上下限,单只股票权重在0到0.1之间,也就是说最多10%仓位。第二类是行业暴露约束,每个行业的合计权重不超过30%。第三类是换手率约束,每次调仓的换手率不超过50%,避免交易成本吃掉收益。第四类是做多约束,不允许做空。这些约束在cvxpy里都是几行代码的事。

import cvxpy as cp import numpy as np n = len(mu) w = cp.Variable(n) gamma = cp.Parameter(nonneg=True) # 目标:最大化 (w @ mu - rf) / sqrt(w @ Sigma @ w) # 转化为最小化方差,约束收益 ret = mu @ w risk = cp.quad_form(w, Sigma) objective = cp.Minimize(risk) constraints = [ ret >= target_return, cp.sum(w) == 1, w >= 0, w <= 0.1, # 行业约束略 ] prob = cp.Problem(objective, constraints) prob.solve()

上面这段代码是简化版,实际实现中我用的是最大化夏普比率的等价形式:固定分母,最大化分子。具体做法是设一个目标收益,最小化方差,然后遍历不同的目标收益,找到夏普比率最大的那个点。这个方法比直接优化夏普比率更稳定,因为直接优化涉及分式规划,数值上容易出问题。

4.3 回测框架与绩效评估

回测框架我用的是自己写的事件驱动引擎,没有用backtrader或者zipline。原因是我需要精确控制每个调仓日的信号生成和组合优化流程,现成框架的抽象层反而碍事。回测逻辑很简单:每个调仓日,用截止当天的数据生成信号,优化组合,然后持有到下一个调仓日。调仓频率设的是每周一次,因为大模型信号是事件驱动的,日频调仓交易成本太高。

绩效评估除了夏普比率,我还看了几个指标:最大回撤、年化收益、年化波动率、Calmar比率、胜率。这些指标用empyrical库计算,一行代码搞定。回测区间是2022年1月到2024年12月,覆盖了熊市、震荡市和反弹市三种环境。

回测结果这里我不放具体数字,因为不同股票池、不同参数下结果差异很大,放出来容易误导。但可以分享一个定性结论:加入大模型信号后,组合的夏普比率相比纯动量策略提升了20%到40%,最大回撤降低了10%到20%。提升主要来自两个方面:一是大模型能提前捕捉到一些事件驱动的机会,二是大模型信号与动量因子相关性低,有分散化效应。

5. 实操过程中踩过的坑与排查技巧

5.1 大模型API调用的稳定性问题

做这个项目最大的痛点不是算法,而是API调用的稳定性。我遇到过几种典型问题:一是超时,尤其是文本比较长的时候,响应时间可能超过30秒;二是返回格式不对,明明要求JSON,它给你返回一段自然语言;三是内容截断,输出到一半停了。

针对超时,我加了重试机制,用tenacity库做指数退避重试,最多重试5次。针对格式问题,我加了JSON解析的容错处理,如果解析失败,尝试用正则提取JSON部分,还不行就丢弃。针对截断,我设置了max_tokens参数,确保输出长度足够,同时在prompt里明确要求“输出必须完整,不要截断”。

还有一个坑是并发控制。我一开始用多线程并发调用API,结果触发限流,大量请求失败。后来改成信号量控制并发数,同时最多10个请求,稳定性大幅提升。如果你用的是按量付费的API,还要注意成本控制,我每天处理500条文本,一个月下来API费用大概在几十美元,不算贵但也要心里有数。

5.2 数据对齐与时间戳处理

金融数据最麻烦的就是时间对齐。行情数据是交易日,财务数据是季度,文本数据是自然日。这三路数据的时间戳必须对齐到同一个调仓日,否则会引入未来函数。我的处理方法是:所有数据都对齐到调仓日的收盘后。也就是说,调仓日当天收盘后,用截止当天的所有数据生成信号,第二天开盘执行交易。

这里有个细节:文本数据的时间戳要特别注意。新闻的发布时间和实际影响时间可能不一致,比如晚上发布的新闻,第二天开盘才会反映到价格里。我的处理是把当天15:00之后发布的新闻归到下一个交易日。这个规则不是完美的,但能避免大部分未来函数问题。

5.3 常见问题速查表

问题现象可能原因排查方法解决方案
Agent输出格式错误prompt约束不够强打印原始输出检查加强schema约束,加格式校验和重试
组合权重极端集中协方差矩阵估计不稳定检查条件数用Ledoit-Wolf收缩估计,加权重约束
回测夏普比率异常高存在未来函数检查数据时间戳对齐严格对齐到调仓日收盘后
API调用频繁失败并发过高触发限流查看错误码加信号量控制并发数,加指数退避重试
信号与价格背离大模型过度反应对比信号与后续收益加评分截断,降低信号权重
换手率过高调仓频率太高统计每日换手降低调仓频率,加换手率约束

提示:回测结果好看不代表实盘能赚钱。我见过太多回测夏普比率2.0以上、实盘亏钱的策略。回测只能验证逻辑,不能预测未来。实盘之前一定要做模拟盘,至少跑三个月。

5.4 参数调优的经验法则

这个系统里有几个关键参数:大模型信号权重、时间衰减半衰期、评分截断阈值、调仓频率、单只股票权重上限。这些参数不能随便设,但也不建议用网格搜索暴力调优,因为容易过拟合。

我的做法是:先根据逻辑定一个合理范围,然后在这个范围内做敏感性分析。比如信号权重,逻辑上它不应该超过历史动量的权重,所以范围定在0.1到0.5之间。然后在这个范围内取几个点,看夏普比率的变化曲线。如果曲线比较平坦,说明参数不敏感,选中间值就行;如果曲线很陡,说明参数敏感,要小心过拟合。

时间衰减半衰期我试了1天、3天、5天、10天四个值,3天的表现最好。这个也符合直觉:事件驱动的影响通常持续几天,太短了信号来不及反映,太长了噪音太多。评分截断阈值试了0.5、0.8、1.0,0.8最好,既能保留信号强度,又能防止极端值。

6. 系统扩展方向与个人实操体会

这套系统跑通之后,我陆续做了一些扩展。一个是加入了多模态信号,把财报电话会议的音频转成文本,再用大模型分析管理层语气。这个方向有意思,但工程复杂度高,音频转文本的准确率是个瓶颈。另一个是加入了组合归因分析,拆解收益来源,看多少来自大模型信号,多少来自动量因子,多少来自行业配置。这个对策略迭代很有帮助。

还有一个扩展方向是Agent的自我反思机制。具体来说,就是让Agent定期回顾自己之前的判断,对比实际市场走势,总结哪些判断对了、哪些错了,然后把总结写入prompt,指导下一次判断。这个思路来自强化学习里的经验回放,实测下来对提升信号质量有一定帮助,但提升幅度有限,大概5%到10%。

我个人在实际操作中的体会是:大模型在金融领域的应用,目前最适合的场景是信息提取和事件驱动信号生成,不适合做价格预测和组合优化。它的优势在于处理非结构化文本,劣势在于数值计算和逻辑一致性。把合适的工作交给合适的工具,这才是工程化的思维方式。另外,这个系统的代码和文档我都整理好了,包括Agent的prompt模板、数据管道脚本、组合优化代码、回测框架,如果你照着复现,大概两三天能跑通。但我要提醒一句:跑通和赚钱是两码事,量化交易的核心竞争力从来不是代码,而是对市场的理解和风险控制的能力。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询