我用 stock-sdk 做了一个纯前端的 A 股看板
先交代一下背景:我平时看盘有个“坏习惯”——开一堆 APP/网页,然后在它们之间疯狂切换,最后得到的不是信息,而是焦虑。
所以干脆自己写一个:打开一个页面,把常用的行情、板块、分时、K 线、资金面、筛选工具都塞进去。
这个项目就是 stock-dashboard:React + TypeScript + Vite 的前端看板。数据源全部来自 stock-sdk,没有后端、没有 Python 定时脚本,就是纯前端直接拉数据。
体验地址我直接放这: stock-dashboard (如果你也想摸鱼,记得开小窗)。

先说最关键的:数据层怎么接的?
项目里我把 stock-sdk 的调用统一收口到了 src/services/sdk.ts。
这里做了三件很“工程化但不装”的事:
SDK 单例 + 重试
new StockSDK({ timeout, retry }),网络抖一下、接口偶发超时这种事就交给 SDK 自己兜底(最多重试 3 次,指数退避那套)。内存缓存(TTL)
行业/概念列表这种 10 秒内不会突然“宇宙大爆炸”的数据,缓存一下省请求;实时行情则是 2~3 秒 TTL,既不会太旧,也不会把接口当压力测试工具。页面只认服务层,不直接碰 SDK
你在src/pages/**里基本看不到new StockSDK(),页面只调用getFullQuotes / getTodayTimeline / getKlineWithIndicators ...这些封装后的方法(类型会从stock-sdk里导入)。
顺便贴两段“骨架代码”,后面每个功能都是围绕它转的:
1 | // src/services/sdk.ts |
1 | // src/services/sdk.ts |
模块和功能:每一块都是怎么用 stock-sdk 拿数据的?
路由都在 src/router/index.tsx,页面基本按功能拆在 src/pages/*。下面按“用户能点到的地方”来讲。
1) 顶部搜索:别让我翻代码查股票
入口在 src/components/layout/Header.tsx,核心就是一行:
search(keyword)→stock-sdk的sdk.search(keyword)
我做了 300ms 的输入防抖,结果列表支持股票/板块混搜;点一下直接跳转:
- 行业板块:
/boards/industry/:code - 概念板块:
/boards/concept/:code - 个股:
/s/:code
顺便把搜索历史塞进了 localStorage(src/services/storage.ts),这点很像“你以为你在找股票,其实你在找昨天的自己”。
2) 总览 Dashboard:打开就能看个大概(以及自选快照)
页面在 src/pages/Dashboard/Dashboard.tsx。
它拿数据很直接:
- 指数行情:
getFullQuotes(MAIN_INDICES)(上证、深成指、创业板、科创 50…) - 行业/概念列表:
getIndustryList()+getConceptList() - 自选快照:从
src/services/storage.ts取自选代码,再getFullQuotes(watchlistCodes.slice(0, 50))
然后用 usePolling 每 5 秒轮询一次(src/hooks/usePolling.ts),页面不可见还会自动暂停,避免你切到别的标签页它还在疯狂刷新。
小插曲:Dashboard 里有个“榜单”区域目前主要还是板块数据的延伸(全市场个股榜要做的话,思路就是上 getAllAShareQuotes,后面“一日持股法”已经把路走通了)。
3) 热力图 Heatmap:今天到底谁在“发热”?
页面在 src/pages/Heatmap/Heatmap.tsx,用 ECharts 的 treemap 做热力图。

按维度不同,数据来源也不同:
- 行业热力图:
getIndustryList()(每个行业自带涨跌幅、换手、领涨股等字段) - 概念热力图:
getConceptList() - 自选热力图:
getAllWatchlistCodes()→getAllQuotesByCodes(codes.slice(0, topK))
如果你把维度切到“个股”(目前代码里留了入口但暂时没放开),我这边的思路是:
- 先
getIndustryConstituents(industryCode)拿成分股 - 再
getAllQuotesByCodes(stockCodes)拉行情 - 拼起来做 treemap
热力图这个功能我最喜欢的一点是:你不用盯着涨跌榜刷屏,颜色一铺开,强弱结构一眼就出来了。
4) 榜单 Rankings:卷不过就看别人怎么卷
页面在 src/pages/Rankings/Rankings.tsx。

这里其实很“偷懒但有效”:
- 先
getIndustryList()/getConceptList()拿到板块列表 - 然后前端按
changePercent/turnoverRate排序,取 Top 50
也就是说它的榜单是“板块榜”,不是“全市场个股榜”。要做全市场个股榜其实也不难(后面“一日持股法”就是全市场路线)。
5) 板块列表 + 板块详情:想知道“这波是谁带的节奏”
板块列表在 src/pages/Boards/Boards.tsx:
getIndustryList()+getConceptList()一次拿齐- 切 tab 只是前端切换数组
- 支持搜索板块名/领涨股
板块详情在 src/pages/Boards/BoardDetail.tsx,这里用到的 API 就比较“齐活”了(按行业/概念分流):
- 详情信息:还是从
getIndustryList()/getConceptList()里 find 出来(少一次请求) - 成分股:
getIndustryConstituents(code)/getConceptConstituents(code) - 板块 K 线:
getIndustryKline(code, { period })/getConceptKline(code, { period }) - 板块 Spot 指标:
getIndustrySpot(code)/getConceptSpot(code)
板块 K 线我只保留了最近 60 根(slice(-60)),不然你拖动 dataZoom 的时候会明显感觉浏览器开始“喘”。
6) 自选 Watchlist:我盯的不是股票,是“我的偏见”
页面在 src/pages/Watchlist/Watchlist.tsx,自选分组/增删改都在 src/services/storage.ts。
行情获取靠:
getAllQuotesByCodes(normalizedActiveCodes)
这里有个小细节:我把股票代码做了 normalizeStockCode(src/utils/format.ts),避免 SZ000001、sz000001、000001 这种“同一个人换三套马甲”导致重复/取不到数据。
7) 个股详情 StockDetail:该看的都给你,看不看的也顺便给你
页面在 src/pages/StockDetail/StockDetail.tsx,这是全项目里最“重”的页面之一,因为信息密度高。

它分别拿这些数据:
- 实时行情:
getFullQuotes([code]) - 分时(1 分钟):
getTodayTimeline(code) - 分钟 K(5/15/30/60):
getMinuteKline(code, { period }) - 日/周/月 K + 技术指标:
getKlineWithIndicators(code, { period, adjust: 'qfq', indicators }) - 资金流:
getFundFlow([code]) - 盘口大单:
getPanelLargeOrder([code])
我比较喜欢 getKlineWithIndicators 这个点:页面上勾选 MA/MACD/BOLL/KDJ/RSI,后端(准确说是 SDK)直接把指标结果算好塞回来了,前端只需要画线,不用自己写一坨技术指标计算(少写 bug,多活几年)。
同样配了轮询:
- 行情 2 秒一刷
- 分时 3 秒一刷
- 资金 10 秒一刷
8) 信号扫描 Scanner:给我一点“量化的幻觉”
页面在 src/pages/Scanner/Scanner.tsx。
扫描流程大概是:
- 先选股票池来源
- 自选:本地拿代码
- 行业/概念:用
getIndustryConstituents('BK0475')/getConceptConstituents('BK0891')抽一批成分股
- 对每只股票调用:
getKlineWithIndicators(code, { indicators: { ma/macd/rsi/boll } })
- 前端用最近两根 K 线做信号判断(MA 金叉、MACD 金叉、RSI 超买超卖…)
这个功能的心理作用大于实际作用,但它确实能帮我把“我觉得它要涨”变成“它真的触发了某个条件”(哪怕条件是我自己写的)。
9) 设置 Settings:不拉行情,只管体验
页面在 src/pages/Settings/Settings.tsx。

这页不调用 stock-sdk,它只负责把刷新频率、涨跌配色、指标默认参数这些偏好写进 localStorage(src/services/storage.ts),让你下次打开页面不至于“重置成出厂设置”。
重点:一日持股法(尾盘选股)= 前端全市场扫描的“重武器”
页面在 src/pages/EndOfDayPicker/EndOfDayPicker.tsx,我在里面实现了一个“三段式”流程,核心就是 getAllAShareQuotes:

第一步:全量拉取 A 股行情(5000+)
1 | // src/pages/EndOfDayPicker/EndOfDayPicker.tsx |
这段调用背后对应的是 stock-sdk 的:
sdk.getAllAShareQuotes(options?: GetAllAShareQuotesOptions): Promise<FullQuote[]>GetAllAShareQuotesOptions支持:batchSize:单次请求股票数量(默认 500)concurrency:最大并发数(默认 7)onProgress:进度回调
我这里把并发设成 5,属于“别太狂,浏览器和网速都要面子”的保守路线;配合 onProgress,页面上能实时展示进度条,不会让你以为网页卡死了。
第二步:用基础条件先砍一刀
全市场 FullQuote[] 到手后,先按这些字段筛一遍(都是从 FullQuote 里直接拿):
- 流通市值
circulatingMarketCap - 量比
volumeRatio - 涨跌幅
changePercent - 换手率
turnoverRate - 以及是否过滤
ST/*ST
这一步在 filterStocksBasic(),做完基本能从 5000+ 砍到几十/几百只,不然后面拉分时会把自己送走。
第三步:对候选股拉分时,算“价格在均线之上”的比例
对基础筛出来的候选,我会再逐批拉分时:
getTodayTimeline(fullCode)(注意这里要拼sh/sz/bj前缀)
然后用分时里 price 和 avgPrice 做一个很直白的强度指标:
price >= avgPrice的点数 / 总点数 =timelineAboveAvgRatio
我在 filterWithTimeline() 里把分时请求按 batchSize = 5 分批并发(不是 SDK 的那个 batchSize,是我自己控制的),防止你一口气对几百只股票同时发请求,最后浏览器先躺平。
最后结果按 timelineAboveAvgRatio 排序,页面上每只股票还带一张迷你分时图,基本就能完成“尾盘快速过一遍候选”的目标。
结尾:这玩意儿适合谁?
如果你想要的是“一个能看、能筛、还能顺手把自选管理了”的轻量看板,而且你不想为此养一个后端,那这个项目的思路就挺合适:用 stock-sdk 把数据能力直接带进前端,然后在 UI 里做组合、筛选和展示。
本地跑起来也很简单:
1 | yarn install |
最后的最后:页面底部那句“仅供学习参考,不构成投资建议”我不是摆设——毕竟写代码可以自信,买股票还是得谦虚点。
相关链接
Happy Coding & Trading!
