news 2026/9/25 5:24:58

Vibe-Trading screen_market 实战解析:从 clist 端点到排行榜的完整调用链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vibe-Trading screen_market 实战解析:从 clist 端点到排行榜的完整调用链路

Vibe-Trading screen_market 实战解析:从 clist 端点到排行榜的完整调用链路

【免费下载链接】Vibe-Trading"Vibe-Trading: Your Personal Trading Agent"项目地址: https://gitcode.com/GitHub_Trending/vi/Vibe-Trading

Vibe-Trading 里有一个只读工具screen_market:一次调用即可拿到 A 股、美股、港股全市场按涨跌幅、成交量、成交额、换手率排名的 Top N。读完本文你会掌握四件事:端点寻址与 fs 市场全集选择器、fid 排序字段映射、返回信封结构、按 IP 限流下的防护机制。

为什么不做逐 symbol 抓取

"今天谁涨得最猛、成交最活跃"是交易研究里最高频的问题。最直接的解法是遍历候选列表逐个拉行情——慢,而且每加一个 symbol 就多一次上游请求,很容易撞上免费数据源的限流墙。

screen_market换了个思路:直接问东方财富 push2 体系的榜单列表接口。给定一个市场全集选择器,服务端返回每只上市标的一行(最新价 + 常用排名指标),并且由服务端按指定字段预排序,客户端只取前 N 行。一个请求顶几百次逐标的抓取,响应体规模天然有界。

架构位置方面:

  • 实现文件 agent/src/tools/market_screener_tool.py,类MarketScreenerTool继承自 agent/src/agent/tools.py 中的BaseTool(L13-55);
  • 注册走 agent/src/tools/__init__.py 的自动发现:_discover_subclasses(L34)用BaseTool.__subclasses__()(L64)递归收集所有子类,放进src/tools/就自动被 LLM 可用,无需手动登记;
  • 技能索引页 agent/src/skills/eastmoney/SKILL.md 的"选股检索"分类(L93-97)里登记了它的用途与适用市场。

Vibe-Trading screen_market 怎么取数:clist 端点与 fs 选择器

端点

整个工具只打一个 URL,模块级常量_CLIST_URL(market_screener_tool.py L30):

https://push2.eastmoney.com/api/qt/clist/get

clist与项目里另一个东财端点分工明确:push2his 的 kline 接口(见 agent/backtest/loaders/eastmoney_client.py 的fetch_kline,L300)回答"某只标的的历史走势",clist回答"全市场谁排前面"。

市场全集选择器(fs)

三个市场的fs值原样保存在_MARKET_FS(L35-39):

源码键fs 实际取值覆盖范围
am:0+t:6,m:0+t:80,m:1+t:2,m:1+t:23,m:0+t:81+s:2048深市主板、创业板、沪市主板、科创板、北交所
usm:105,m:106,m:107NASDAQ、NYSE、AMEX
hkm:116,m:113,m:114,m:115,m:128港股主板与创业板等

这些片段对应东财的 secid 寻址约定:沪市用市场号1、深市/北交所用0(与resolve_secid中 A 股1.<code>/0.<code>的写法一一对应);港股固定116前缀 + 5 位零填充代码;美股三所分别是105/106/107。

排序字段映射:sort_by → fid → 查询参数

fid 映射

_SORT_FID(L43-48)把对外的排序名翻译成东财字段号:

sort_byfid含义
change_pct(默认)f3涨跌幅 %
volumef5成交量(手)
amountf6成交额(货币单位)
turnoverf8换手率 %

排序方向恒为降序——"榜单"语义就是取最大的前 N 名,所以方向参数写死不提供入口。

完整的查询参数拼装

_screen_market(L120-164)调用get_json时逐项拼装 params(L137-147):

参数取值说明
pn1页码,固定第一页
pzstr(top_n)每页条数
po11 = 降序
fid_SORT_FID[sort_by]服务端排序依据
fs_MARKET_FS[market]市场全集
fieldsf2,f3,f4,f5,f6,f8,f12,f14请求返回的列(_FIELDS,L52)

测试test_sort_by_maps_to_eastmoney_fid(agent/tests/test_market_screener_tool.py L80-90)断言了这条拼接链路:market="us", sort_by="amount"时请求参数中fid == "f6"、po == "1"、fs == "m:105,m:106,m:107"。

一行原始数据怎么变成规范记录

响应体里data.diff是行列表,每行是 field id 键控的 dict,_shape_row(L92-117)负责归一化:

field id输出键描述注意事项
f12code代码缺失则整行丢弃
f14name名称缺省为空串
f2price最新价经_num转换
f3change_pct涨跌幅同上
f4change涨跌额同上
f5volume成交量(手)同上
f6amount成交额同上
f8turnover_rate换手率 %同上

两个细节值得展开:

  1. 哨兵值映射为 None。东财对无值单元格返回"-",_num(L71-89)将其转成None而不是 0.0——停牌股的换手率是"没有",不是"零"。测试test_change_pct_screen_parses_rows(L46-78)里平安银行的f8就是"-",断言结果为turnover_rate is None。
  2. diff 结构双形态归一化。同一接口在不同主机上data.diff可能是 list,也可能是按索引键控的 dict(键为"0"、"1"……)。_screen_market对 dict 形态执行list(diff.values())(L154-155),测试test_diff_as_dict_is_normalized(L92-101)专门覆盖了这条分支。

入参与校验:三个参数的合法边界

JSON Schema 定义在MarketScreenerTool.parameters(L179-210):

参数类型必选合法取值默认值
marketstr是a/us/hk无
sort_bystr否change_pct/volume/amount/turnoverchange_pct
top_nint否1–100 的正整数30(_DEFAULT_TOP_N,L56)

execute(L213-256)的校验边界行为:

  • market 白名单:非字符串或不在_MARKET_FS键集中即返回"market must be one of ['a', 'us', 'hk']";
  • sort_by 白名单:同上逻辑,报错信息列出四个合法键;
  • top_n 的布尔陷阱:校验写的是not isinstance(top_n, int) or isinstance(top_n, bool)(L236)——Python 里isinstance(True, int)为真,必须显式排除布尔,测试test_bool_top_n_rejected(L150-153)验证top_n=True会被拒;
  • 上限钳制:通过校验后top_n = min(top_n, _MAX_TOP_N)(L238),_MAX_TOP_N = 100(L55),传 500 也只拿 100 行,绝不报错;
  • 兜底捕获:请求阶段的任何异常(含 429)在execute里被捕获并转成错误信封(L240-244),测试test_http_failure_surfaces_as_error_envelope(L155-164)模拟RuntimeError("HTTP 429")验证异常不外抛。

返回信封长什么样

成功时返回 JSON 字符串(ensure_ascii=False,中文名不转义):

{ "ok": true, "market": "a", "source": "eastmoney", "data": { "market": "a", "sort_by": "change_pct", "rows": [ {"code": "600519", "name": "贵州茅台", "price": 1688.0, "change_pct": 9.98, "change": 153.0, "volume": 1234567.0, "amount": 2080000000.0, "turnover_rate": 1.23}, {"code": "000001", "name": "平安银行", "price": 11.5, "change_pct": 5.01, "change": 0.55, "volume": 9876543.0, "amount": 110000000.0, "turnover_rate": null} ] } }

失败时(参数校验或请求异常):

{"ok": false, "error": "top_n must be a positive integer"}

行列表嵌在data.rows下而非裸列表,源码 docstring 明确写了原因(L224-225):与项目内所有工具的data:{...}信封形状保持一致,下游解析代码可以统一处理。即便上游返回空数据,也是ok: true+ 空rows(测试test_rowless_payload_yields_empty_data,L111-122),不会退化成裸列表。

为什么不该裸调端点:共享限速层

东财按源 IP 限流,并对突发请求的客户端临时封禁。工具的每次请求都不自己发 HTTP,而是走 agent/backtest/loaders/eastmoney_client.py 的get_json(L112-133),后者固定以host_key="eastmoney"(_HOST_KEY,L38)进入 agent/backtest/loaders/_http.py 的节流体系:

  • 进程级节流器:HostThrottle(L46-106)按 host 桶记录上次发枪时刻,保证同桶相邻请求最小间隔;并发调用方链式排队,锁只持有记账期间,不同桶互不阻塞;
  • 随机抖动:间隔之上叠加最多 0.4 秒抖动(_JITTER_MAX_S,L43),避免多个并发调用方同时到期齐射;
  • Session 复用:每进程每桶一个requests.Session(L114-124),摊薄 TCP/TLS 握手;
  • 浏览器 UA:默认携带桌面 Chrome 的 User-Agent(L35-38),因为不少免费行情端点拒绝裸 requests 指纹;
  • 最小间隔可调:默认 1.0 秒,环境变量可覆盖:
VIBE_TRADING_EASTMONEY_MIN_INTERVAL

批量任务建议调大而不是调小。这意味着绕开工具直连clist端点的突发请求会失去节流保护,可能触发按 IP 的临时封禁——连累的是同 IP 下所有走共享节流层的调用方,包括 backtest 的东财 loader。

直接调用与 swarm 中的用法

以下示例在agent/目录下运行,导入根为agent/,无需 token:

# 场景 1:A 股今日涨幅榜前 20 from src.tools.market_screener_tool import MarketScreenerTool print(MarketScreenerTool().execute(market="a", sort_by="change_pct", top_n=20))
# 场景 2:美股成交额榜前 10 print(MarketScreenerTool().execute(market="us", sort_by="amount", top_n=10))
# 场景 3:港股换手率榜前 50 print(MarketScreenerTool().execute(market="hk", sort_by="turnover", top_n=50))

repeatable = True(L211)允许同一轮对话里多次调用对比不同市场/排序。技能脚本 agent/src/skills/eastmoney/scripts/screen_search_example.py 演示了"先search_symbol解析标的、再screen_market看市场动向"的完整研究流程。

在 swarm 多 Agent 编排中,它是研究型 worker 的 universe 枚举工具:

  • agent/src/swarm/presets/statistical_arbitrage_desk.yaml(L48):先screen_market枚举当日热点,再get_market_data拉价格面板;
  • agent/src/swarm/presets/pairs_research_lab.yaml(L60):用screen_market枚举{market}/{sector}候选池后再逐对扫描。

它不能做什么

  • 数据范围:只有 A 股、美股、港股三大市场的行情列表,期货、期权、外汇不在覆盖内;
  • 实时性:返回的是 push2 行情快照,适合当日排名类问题;历史榜单序列不在此工具职责内;
  • 单位语义:volume以"手"计,amount为货币单位成交额,turnover_rate是百分比;跨市场比较时单位口径不同(港股量纲与 A 股不同);
  • 空值语义:停牌等无值场景对应字段是None而非 0,下游计算必须做空值防御;
  • 限流依赖:可靠性建立在共享节流层之上,节流是进程内 best-effort、不跨机器协调,高并发场景靠环境变量拉开间隔。

相关工具

  • agent/src/tools/symbol_search_tool.py 的search_symbol:把公司名/代码片段解析为候选 symbol,与screen_market组成"模糊查询 → 全市场扫描"的组合拳;
  • agent/backtest/loaders/eastmoney_client.py 的fetch_kline:入选标的的历史 K 线拉取,榜单粗筛后的深度数据环节。

【免费下载链接】Vibe-Trading"Vibe-Trading: Your Personal Trading Agent"项目地址: https://gitcode.com/GitHub_Trending/vi/Vibe-Trading

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 5:23:52

8051单片机Keil uVision2 C51开发指南:安装、内存模型与调试避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 5:22:22

5分钟跑通自托管 AI 伴侣:AIRI 实时语音与游戏陪玩完整指南

5分钟跑通自托管 AI 伴侣&#xff1a;AIRI 实时语音与游戏陪玩完整指南 【免费下载链接】airi &#x1f496;&#x1f9f8; Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-s…

作者头像 李华
网站建设 2026/9/25 5:21:37

Cyrus SASL 2.1.21 编译部署与排错指南:从源码到认证链路

简介&#xff1a;这是一份 Cyrus SASL 2.1.21 开源认证库的源码压缩包&#xff0c;面向邮件服务器管理员、安全运维人员以及有二次开发需求的嵌入式开发者。它主要服务于 SMTP、IMAP、POP3 等协议场景&#xff0c;提供多种可插拔的认证机制&#xff0c;是 Postfix 等邮件传输代…

作者头像 李华
网站建设 2026/9/25 5:14:40

LS-DYNA多节点计算的许可证配置与故障排查实战

1. 先搞清楚问题&#xff1a;为什么LS-DYNA多节点计算老是卡在许可证上这些年我经手过不少LS-DYNA的部署和算例优化&#xff0c;发现一个特别普遍的现象&#xff1a;很多工程师拿到一套新配置&#xff0c;第一反应是把求解器的关键字文件调好、把CPU核数拉到满&#xff0c;然后…

作者头像 李华