构建AI驱动的股票筛选器
我花了两个月时间构建了一个代理AI研究工具,用于筛选国际股票。它的运行成本约为10美元,拒绝了大约95%的候选股票,并让我能够将更多精力集中在真正关心的股票上。
本文详细介绍了代理AI系统的工作原理、它做得好的地方、它遇到的困难,以及在构建类似东西之前你应该知道的事情。
1、问题
白天我在数据工程和信息安全领域工作。
然而,最近我花了很多晚上的时间思考,美国投资者担心赤字、AI泡沫和总体政治不稳定,可以通过分散投资到非美国市场的中盘股来对冲这些风险——例如东京、香港、华沙和吉隆坡等市场,这些市场中有合法、增长中的中小盘公司交易并赚钱,而不严重依赖美国市场和贸易。
由于符合这一理论的股票通常没有被美国分析师充分覆盖,找到和评估它们既困难又需要时间,而我没有时间。这也会花费金钱。Bloomberg终端约3万美元/年。FactSet也差不多。是的,你可以把你的投资组合交给像Fisher这样的经理人,但他们会收取一定费用。
随着AI现在开辟了新的可能性,我想:我能否低成本地构建一个代理AI股票研究工具,帮助我找到和筛选符合我理论的股票并生成轻松的alpha?
2、我构建的解决方案
我最终构建的股票评估器使用LangGraph(一个代理协调框架)来运行五阶段工作流程。
阶段1:数据收集(并行)
四个专业代理同时运行:
- 市场分析师:价格历史、流动性、RSI/MACD/Bollinger
- 基本面分析师:市盈率、净资产收益率、负债权益比、现金流等
- 新闻分析师:最近的文件、公司新闻(Tavily)
- 情绪分析师:零售热度(StockTwits、Reddit)
这在东京、香港、台湾和其他成熟交易所中效果很好。但在马来西亚、印度尼西亚和大部分东南亚地区效果较差,因为免费的数据源在这些地区的交易所上市数据并不完善。
以下是实际的后备逻辑:
def get_financial_metrics(ticker: str) -> dict:
"""尝试多个来源,合并结果,填补关键空白。"""
result = {}
# 主要来源:yfinance(免费,通常对于流动性强的股票很好)
result = fetch_yfinance(ticker)
# 如果关键指标缺失,尝试yahooquery
if missing_critical_fields(result):
yahoo_data = fetch_yahooquery(ticker)
result = merge_keeping_best(result, yahoo_data)
# 如果仍然有空白,尝试FMP(如果API密钥可用)
if missing_critical_fields(result) and FMP_API_KEY:
fmp_data = fetch_fmp(ticker)
result = merge_keeping_best(result, fmp_data)
# 最后手段:针对特定缺失字段进行网络搜索
if missing_critical_fields(result):
web_data = search_for_gaps(ticker, result)
result = merge_keeping_best(result, web_data)
return sanitize_and_validate(result)
主要问题:搜索某些股票,特别是那些仍处于标准会计和透明度要求概念中的国家的交易所,返回空的结果集。当这种情况发生时,后续代理可利用的信息就更少了。
阶段2:综合
研究经理尽最大努力处理所有四份报告并撰写摘要。在我早期的代码版本中,此阶段的输出是一篇超过2000字的文章。不受限制的LLM可能会非常冗长。通过积极的提示工程,我将其长度减少到几个要点(“不要写引言”)。
阶段3:多空辩论(变革性部分)
在此阶段,两个对抗代理进行1–2轮辩论。多头得到一个提示,告诉它“为购买提出最强的论点——反驳空头的担忧。”空头则被指示“为避免购买提出最强的论点——并反驳多头的乐观情绪。”
来自汇丰银行(0005.HK)的例子:
多头:“市盈率14.79,低于历史平均水平。40%的营业利润率。610亿美元的自由现金流。这是便宜的,而不是破败的。”
空头:“营收增长4.8%是疲软的。每股收益同比下降16.6%。在地缘政治不确定时期在港运营。一家成熟的银行,没有增长故事。坚决拒绝。”
投资组合经理阅读双方的论点并应用评分规则。上述案例中,汇丰银行未能满足增长要求→卖出。
基于辩论的评估结构优于让单个LLM“分析这只股票”。单代理会进行权衡和妥协。他们也可能被你提示中无意的引导语言所影响。对抗代理让他们承诺不同的立场,并允许后续代理清楚地看到辩论的所有方面。
阶段4:风险规模
一旦研究经理完成其输入的综合,三个风险分析师(保守、中立和激进)然后推荐一个仓位规模。即使经过推动,我发现“激进”的一方可能非常谨慎——Gemini似乎被训练得风险规避。但这对我来说没问题。我也很谨慎。
最终决定
在此阶段,投资组合经理设置硬性门槛:
- 财务健康评级 ≥ 50%
- 增长评分 ≥ 50%
- 流动性 ≥ 每日50万美元
- 分析师覆盖率 < 15(更喜欢未被发现的名称)
例如,截至我2025年11月30日的运行,汇丰银行在健康方面得分为62.5%,增长得分为0% → 自动卖出。三星在估值方面表现良好,但有100多名分析师谈论它 → 卖出。
当然,这就是想法。该系统强制执行我关心的规则,并仅显示不违反规则的内容。它节省了我的时间。
在上述情况下,逻辑如下:
def make_decision(health_score, growth_score, liquidity, coverage):
# 硬性失败 - 任何一项都会使股票不合格
if health_score < 0.50:
return "SELL", "硬性失败:财务健康 < 50%"
if growth_score < 0.50:
return "SELL", "硬性失败:增长评分 < 50%"
if liquidity < 500_000:
return "SELL", "硬性失败:流动性 < 50万美元"
if coverage >= 15:
return "SELL", "分析师覆盖率过高(未被发现)"
# 通过所有门 - 现在检查软因素(估值、风险)
return assess_qualitative_factors(...)
无论多少辩论都无法改变这些阈值。如果增长为0%,那就是卖出。
3、为什么有效
1. 它强制纪律汇丰银行的市盈率为14.79,看起来很便宜。它是汇丰银行——全球银行,家喻户晓,支付股息。人类分析师可能会想,“但它就是汇丰银行。我听了他们的Banyan Tree播客。他们很棒!”AI不关心。增长评分:0%。被拒绝。
如果没有这个系统,我会拒绝汇丰银行吗?我不知道。可能甚至不会想到去查看。系统是对的吗?是的,很可能。如果我担心,我可以在报告中获得所需的所有数据,以便开始检查。
2. 多空结构迫使更深入的思考单一LLM的回应平淡且含糊。多代理辩论迫使每一方做出承诺。这就像维基百科文章(平衡但无聊)与法庭辩论(两个强有力的案例,开放倡导和辩论)之间的区别。
3. 全面审计追踪每个数据点都有来源。每个评分都显示其计算方式。当系统说“健康评分:62.5%(5/12原始,调整为8个可用点)”,你可以看到:
- 它获得了哪5个点
- 哪7个点是N/A(以及原因)
- 精确的数学
信任来自于可以验证。下游AI代理可以看到工作,用户查看最终报告时也能看到。
4. 零边际成本构建之后,使用Gemini免费层级运行100只股票的成本是0美元(每分钟15次请求限制)。我升级到了付费第二层级以加快批量处理和更宽松的API限制,现在300只股票的费用约为10美元。
相比之下:Bloomberg终端是每月2,667美元。
4、什么会可靠地失败
1. 坏数据污染下游结果案例研究:YTLPOWR.KL(YTL Power International,马来西亚)
系统在最后一次运行(2025年11月下旬)返回:
- 市盈率:161,103,004,000.00(是的,1610亿)
- 市净率:27,230,309,079.61(270亿)
- 财务健康评分:0%(所有指标N/A)
YTL Power是一家糟糕的公司吗?我不知道。免费API不涵盖马来西亚股票。yfinance抓取了损坏的数据,FMP返回了空响应,EODHD在此次运行中没有内容(可能是另一个错误,但其他大多数股票运行正常)。
系统正确标记了这些为异常并拒绝了股票。但它是因为数据缺口而拒绝的,而不是因为基本面差。我没有任何关于实际公司的信息。
教训:免费API在成熟市场中表现良好。对于新兴市场(马来西亚、印度尼西亚、泰国等),你需要付费来源或必须将结果视为低置信度标志以供手动研究。
2. 系统过于冗长早期报告超过2000字。每只股票都是如此。多头代理会写道:
“基于对财务报表的全面分析,考虑定量指标和定性因素,同时承认前瞻性预测的固有不确定性…”
我不得不添加提示,如:
- “不要写引言”
- “不要总结你的总结”
- “使用项目符号,而不是段落”
- “每条回复最多300字”
即使有这些限制,报告仍然超过800字。LLM喜欢听到自己说话。
3. 大盘股有时会偷偷通过理论:找到“未被发现”的股票(分析师覆盖率 < 15)。
这对中盘股有效。但对于大盘股,搜索并不总是那么有用:
腾讯(0700.HK):“4000亿美元市值…但只有12名分析师由yfinance跟踪…一定是未被发现的!”
显然,腾讯并不是未被发现的。分析师数量只是我计数的方式(由yfinance跟踪的英语分析师)。
修复:我添加了市值阈值(>500亿美元 = 自动“高覆盖率”假设)。但这只是一个补丁,不是解决方案。真正的问题:系统偏爱大盘股,因为你可以找到它们的数据。目前,马来西亚真正冷门的中盘股存在于数据沙漠中,我的股票评估器在处理它们时遇到了困难。
4. 语言障碍是不可逾越的对于韩国、日本或中国股票,本地情绪很重要。但是:
- StockTwits:仅限英文,以美国为中心
- Reddit:主要是英文
- 本地论坛:需要API访问和我无法拥有的语言技能
结果:大多数非美国股票的“市场情绪”部分说“无法评估本地情绪”。
如果三星在韩国论坛上受到抨击,AI代理可能不知道,除非我手动检查(这违背了让AI代理完成工作的初衷)。
4. 内存隔离花费的时间比预期的更长早期版本显示了一个持续的错误,分析佳能时会污染汇丰银行的分析。AI会在讨论汇丰银行(一家银行)时引用佳能的芯片短缺。
修复方法:
# 之前:全局内存(错误)
bull_memory = ChromaDB(collection="bull_analysis") # 所有股票共享
bear_memory = ChromaDB(collection="bear_analysis")
# 之后:股票特定命名空间(正确)
def create_memories(ticker: str):
safe_name = ticker.replace(".", "_") # 0005.HK → 0005_HK
return {
"bull": ChromaDB(collection=f"{safe_name}_bull"),
"bear": ChromaDB(collection=f"{safe_name}_bear"),
"risk": ChromaDB(collection=f"{safe_name}_risk")
}
现在每只股票都有隔离的内存。佳能的分析不会泄露到汇丰银行的分析中。如果你构建这样的系统,请密切关注保存了什么以及如何保存,以及数据在生成过程中如何清除,以及何时清除。
5. 速度不是功能对于300只股票:
- 快速模式:6–12小时
- 标准模式:16–30小时(随着代码演变而变化)
即使在快速模式下,这也很慢。一个拥有Bloomberg的人可以在30分钟内扫描50只股票,并做一份不错的作业。
权衡:AI很细致(可能太细致了),但目前不够快。
5、我会有所不同(建议)
1. 更好的数据源(可选的未来扩展)我花了60%的时间在提示工程上。我应该花60%的时间寻找更好的数据源。
“好”和“优秀”分析之间的差异是数据质量,而不是代理架构。
对于想要扩展这个的人:该系统设计为使用免费API以保持可访问性。但如果你认真对待新兴市场,付费来源会有帮助:
- Polygon.io(国际覆盖)
- Koyfin(非专业人士每月20美元)
- Alpha Vantage高级版
- 公司IR网站爬虫(自己构建)
目标是保持核心系统免费和开源。付费数据应该是可插拔的扩展,而不是必需品。如果你为这些来源构建连接器,请创建一个pull request——但确保它们是可选依赖项。别忘了为一切编写测试(坏的或恶意的输入、损坏的API、断开的网络连接、超时、速率限制、异常处理等等)。
2. 在辩论之前添加数据质量检查在多空辩论之前,添加一个代理说:
“12个财务指标中有8个可用。数据质量:良好。继续。”
或者
“12个财务指标中有2个可用。数据质量:差。标记为人工审查。”
这会让我避免分析YTL Power(市盈率数据损坏的股票)。
实现:
def check_data_quality(metrics: dict) -> str:
total_fields = 12 # 市盈率、净资产收益率、资产回报率、负债权益比、收入、每股收益等
available = sum(1 for v in metrics.values() if v is not None)
coverage = available / total_fields
if coverage >= 0.66: # 8+ of 12 fields
return "GOOD"
elif coverage >= 0.33: # 4-7 of 12 fields
return "MARGINAL"
else: # < 4 fields
return "POOR"
# 然后在主流程中:
data_quality = check_data_quality(financial_metrics)
if data_quality == "POOR":
return {
"decision": "REJECT",
"reason": "数据分析数据不足",
"metrics_available": f"{available}/{total_fields}"
}
在浪费计算资源进行无价值辩论之前运行。
3. 使用结构化输出(在大规模筛选时)我的代理返回markdown文本。这对于人工研究个别股票来说是可读的,效果很好。
对于批量筛选(筛选300只股票以找到15只买入),我希望有人添加JSON输出作为选项:
{
"ticker": "0005.HK",
"company": "汇丰控股",
"decision": "卖出",
"conviction": "高",
"health_score": 62.5,
"growth_score": 0.0,
"position_size_pct": 0,
"hard_fails": ["growth_score"],
"key_risks": ["收益下降", "地缘政治风险"],
"rationale": "通过健康门但未满足增长要求(0% vs 50%阈值)"
}
然后我们可以程序化过滤:jq '.[] | select(.decision == "BUY")' results.json.
保留markdown用于详细报告。添加JSON用于程序化过滤。两者都有其用途。
4. 仔细选择你的市场我想要一个通用的非美国股票分析器。我学到的是:没有这样的东西。
- 香港/日本/台湾:出色的API覆盖,系统有效
- 马来西亚/印度尼西亚/泰国:数据荒漠,系统挣扎
- 韩国:中间地带(数据不错,语言障碍)
- 大多数欧洲国家:稳固
- 印度、越南、巴西:无法访问美国散户投资者
教训:为你要交易的市场构建。不要试图解决所有问题。
6、这是否有用?
这个系统做得好的地方:
- 将300个候选者筛选到十几个值得调查的
- 捕获许多价值陷阱(便宜是有原因的)
- 强制纪律(硬性门槛防止情绪交易)
- 每次运行成本0–10美元,而不是每年24,000美元
它不能做到:
- 替代人类判断(它不能)
- 处理坏数据(垃圾输入,垃圾输出)
- 在没有付费API的情况下处理新兴市场
- 实时交易决策(它太慢)
把它想象成这样:如果你雇用一个实习生来做初步的股票筛选,这个系统完成了实习生的工作。它不会取代资深分析师,也不会找到数据中没有的机会。但它会让你避免浪费时间在明显的拒绝上。
7、代码是开源的
仓库链接。
包含的内容:
- 完整的LangGraph工作流(6种代理类型)
- 多源数据获取器(yfinance、FMP、EODHD、Tavily)
- 450多个单元测试
- 详细的设置说明
要求:
- Python 3.12+
- Google Gemini API密钥(免费层级可用)
- 基本的命令行技能
- 可选:Tavily、FMP、EODHD密钥以获得更好的数据
你可以用它做什么:
- 筛选你自己的观察清单
- 修改理论(更改阈值,增删规则)
- 交换更好的数据源(请这样做,但使其成为可选)
- 研究多代理系统在实践中的运作方式
你不应该做:
- 盲目信任它用于真实资金
- 期望第一天就完美
- 当推荐不起作用时责怪我
8、最终想法
我构建的代理AI股票评估器的真实价值不在于找到下一个10倍股。而在于有一个系统说“不”给285只股票,让我可以将时间花在剩下的15只上。这值得每月10美元。
如果你在构建类似的东西,我的建议是:首先专注于数据质量,然后在浪费计算资源于垃圾输入之前添加数据置信度检查。如果我再写一次,我可能会使用结构化输出而不是markdown。
对于新兴市场,考虑构建付费API连接器——但保持它们的可选性,这样系统对所有人来说仍然是可访问的,成本低廉。
原文链接:Building an Open-Source Agentic AI Equity Research Tool
汇智网翻译整理,转载请标明出处