用Ticker API写一个行情面板:一次完整的实现过程

用Ticker API写一个行情面板:一次完整的实现过程

在"行情展示"这个场景里,REST Ticker + 定时刷新通常已经能满足需求;这篇我用一个可运行的 Demo,把这件事做出来验证一遍。

在上一篇专栏中,我把行情API的使用拆成了三个阶段:启动阶段 / 展示阶段 / 实时阶段。这篇文章只聚焦第二个阶段,用一个可运行的Demo把它落地。


一、先把页面结构和真实数据跑通

这个Demo要做什么

这次我做的是一个 Ticker 行情面板:把外汇、贵金属、美股、A 股、加密货币放进同一张表里,满足"看一眼行情"的需求。

数据来源用的是TickDB的/v1/market/ticker,目标很简单:能稳定跑起来、能长期用起来。

这不是一个"盯盘页",用户不需要盯着价格跳动做决策,只是"看一眼当前行情"。

先定页面结构

一开始我确实想直接调接口。打开文档,看到/v1/market/ticker,第一反应是:写个fetch,打印JSON,看看数据长什么样。

但我停下来了。因为我还没想清楚:这个页面到底要长什么样。

如果这是一个"盯盘页",那页面结构会完全不同:需要大字号价格、跳动动画、实时连接状态。如果这是一个"列表页",那就是另一回事。

我当时的判断是:这是一个行情展示页面,用户只是"看一眼行情"。

所以我先把页面结构定下来:

  1. Header:标题 + 说明
  2. 控制区:刷新按钮、刷新间隔选择、管理自选
  3. 表格:Symbol、最新价、涨跌、24h高低、量、时间
  4. 状态栏:API状态、延迟、上次更新时间

这个页面没有秒级跳动、动画效果、深度盘口、K线图,但它已经能回答最常见的问题:现在价格是多少?今天涨还是跌?波动范围大不大?不同市场能不能放在一张表里看?

UI结构定下来之后,后面的实现就有了明确的边界。后面所有的工作,都是在这张结构里"往里填东西"。

接入真实数据

页面结构定型后,我把 fetchTicker() 接到 /v1/market/ticker 上,先跑通一次"从请求到渲染"的闭环。

真正麻烦的是:同一张表里,不同市场返回的字段并不一致。有的没有成交量,有的缺少涨跌额/涨跌幅,有的只有买卖价相关字段。

市场类型 有成交量 有涨跌数据 有买卖价
加密货币
股票
外汇/贵金属

如果不做字段容错,页面会直接报错或显示NaN

所以我必须做字段容错:有值就显示,没有值就显示-。这样无论行情接口返回什么数据,表格都能正常显示。

右上角的"延迟"也是这个阶段加的。很多Demo截图看起来很漂亮,但不知道它是不是真的在跑。加一个延迟数字,100ms150ms,这个Demo就不再是"演示品"。

到这里为止,这个面板已经可以稳定地用真实市场数据跑起来了。


二、让行情面板稳定刷新并真正可用

自动刷新不能无脑setInterval

面板能跑了,但如果真的使用起来,马上会遇到一个问题:没人愿意一直点"刷新"按钮。

一开始我以为自动刷新很简单:setInterval调一下fetchTicker()就行了。

但实际跑起来发现:

  • 如果上一次请求还没回来,下一次刷新已经开始了
  • 请求重叠后,数据顺序可能错乱
  • UI状态变得不可信(到底是在刷新,还是卡住了?)

本质上是请求重叠导致的时序问题:上一轮没回来,下一轮又开始了。

我必须引入一个"刷新中"状态来控制节奏:

text 复制代码
  state = {
    isFetching: false,
    nextRefreshAt: null
  }

  function refreshTicker() {
    if (state.isFetching) return

    state.isFetching = true
    fetchTicker()
      .finally(() => {
        state.isFetching = false
        state.nextRefreshAt = now + interval
      })
  }

关键是:刷新行为本身必须是显式、可控的状态,而不是隐形的副作用。

工具栏这一行还做了一件事:显示"下次刷新: 5s",每秒倒计时。

我当时的想法是:当用户能看到"还有3秒刷新",他会知道系统没有卡住、刷新是有节奏的、如果数据没变不是系统坏了而是市场本身没动。

Demo里默认是5秒刷新,但也可以切换3秒或10秒。我当时选5秒的原因是:3秒收益不明显但请求量翻倍,10秒用户会觉得"有点慢",5秒是在"感知延迟"和"系统成本"之间找到的平衡点。

自选列表是前端状态

面板能稳定刷新了,但又会遇到一个问题:没人想看所有Symbol。

用户真正想要的是:"我关心的那几个Symbol"。

一开始我以为自选列表需要后端支持。但实际上,这是一个纯前端状态问题:

text 复制代码
state = {
  watchlist: loadFromStorage()
}

function updateWatchlist(list) {
  state.watchlist = list
  saveToStorage(list)
}

function refresh() {
  fetchTicker(state.watchlist)
}

把watchlist提升为明确的状态后,行情刷新逻辑反而变得更简单:每次刷新只请求watchlist里的Symbol。

我还把自选列表存到localStorage。如果每次刷新页面都要重新选Symbol,用户会直接放弃。

这个决策看起来简单,但它背后有一个判断:自选列表是用户状态,不是行情状态。 它不需要后端支持,不需要账号系统,只需要浏览器本地存储就够了。

到这里为止,这个面板具备了最小可用性:刷新稳定、关注列表可保存、打开就能继续用。


三、补齐可用性与工程完整性

搜索和筛选只是视图层问题

面板能用了,但当我加了几十个产品的时候就会遇到一个问题:找不到想看的那个。

我引入了基础筛选和搜索:市场筛选(只看外汇、只看美股行情、只看加密货币行情)、搜索框(输入关键词,实时过滤)。

我把搜索/筛选限定在视图层:它只改变表格展示的行,不改变请求的 symbols 列表。

这样请求层和渲染层解耦,避免为了 UI 交互去打乱刷新节奏。这样筛选逻辑和行情逻辑就能彻底解耦,互不干扰。

异常状态的处理

做到这里,其实行情面板的"正常路径"已经跑通了。但我很快意识到一件事:
如果这个 Demo 真的要给别人用,异常路径不能空着。

最直接的问题就是,一旦接口出错,现在的页面只会"什么都不显示"。

这在自己调试时还能接受,但对使用者来说,很难判断到底发生了什么。

于是我补了一套最基本的错误状态处理:API Key未配置、请求失败,以及接口返回错误码的情况。同时把底部状态栏的信息也补全了,统一展示API状态、请求延迟和上次更新时间。

逻辑上并不复杂,大致就是把数据请求和渲染包在一层异常处理里:

text 复制代码
try {
  data = fetchTicker()
  render(data)
} catch (err) {
  showErrorState(err)
}

错误码这块我参考了TickDB的错误文档,做了友好提示:1001是API Key无效或已过期,2002是交易品种不存在,3001是请求频率超限。

另外我在底部状态栏加了一个「导出 CSV」的按钮。

当时的想法很简单:如果用户能把当前行情数据直接导出来,自己再做分析或处理,这个 Demo 就不只是"看一眼效果",而是已经具备了最小可用的价值。


四、复盘:行情展示型面板的技术边界

这个Demo用的是REST Ticker + 定时刷新,这是我在这个场景下的选择。

这个面板解决的是什么场景

用户的行为是"看一眼行情",不是"盯着价格跳动做决策"。在这个场景下,5秒刷新已经足够,用户关心的Symbol通常不超过10个,当刷新节奏是可感知、可解释的,用户对"实时性"的焦虑会明显下降。

回头看,这个面板之所以能成立,不是因为选了什么"高级技术",而是每一步都围绕同一个目标:让刷新节奏可控、让状态可解释、让用户能长期用。

在"看一眼行情"的场景里,REST Ticker + 定时刷新就是顺势而为。

WebSocket什么时候才是正确选项

我的判断很简单:当用户的行为从"看"变成"盯"的时候。

具体来说,如果用户只是"看一眼价格",定时刷新够用;如果用户需要"盯着价格变化做决策",才需要WebSocket推送。这不是技术选型问题,而是场景判断问题。

很多行情系统一上来就想做"实时",第一反应是上WebSocket、做秒级刷新、加动画。但实际跑起来会发现:用户根本不需要秒级更新,连接管理、断线重连、消息积压反而成了负担,前端性能问题(DOM频繁更新)比接口延迟更严重。

工程上的专业,恰恰是知道什么时候用什么技术。


附录:如何运行这个Demo

这个Demo是纯HTML + 原生JavaScript,无需构建工具。

这个Demo的代码我已经整理成一个完整仓库,包括页面结构、数据请求、刷新逻辑和异常处理。如果你想直接跑一下、或者对某一步的实现细节更感兴趣,可以在GitHub里看到完整代码:

👉 https://github.com/tickdb/tickdb-demo-ticker-panel

运行步骤:

  1. 打开config.js,填入你的API Key
  2. 直接用浏览器打开index.html即可

常见问题:

  • 看到"请配置API Key"提示,说明config.js未正确配置
  • 数据显示-,说明该市场该字段确实没有数据(这是正常的)
  • 请求失败,检查API Key是否有效、网络是否正常
相关推荐
TickDB官方4 天前
行情API的正确使用方式:从接口调通到系统设计
实时数据·wbsocket·行情api设计·行情系统
涤生大数据1 个月前
放弃Canal后,我们用Flink CDC实现了99.99%的数据一致性
大数据·数据仓库·flink·大数据开发·flink cdc·数据开发·实时数据
云器科技1 个月前
小红书×云器科技|增量计算+实时湖仓构建小红书实验数仓生产新范式
大数据·数据库架构·小红书·实时数据·数据湖仓
Tapdata 钛铂数据5 个月前
TapData vs Kafka ETL Pipeline:竞争?共存?——企业实时数据策略的正确打开方式
kafka·数据同步·实时数据·kafka connect
kngines7 个月前
【力扣(LeetCode)】数据挖掘面试题0002:当面对实时数据流时您如何设计和实现机器学习模型?
机器学习·数据挖掘·面试题·实时数据
PersistJiao1 年前
数仓报表需要支持历史数据和实时数据的整合的场景要如何处理
数仓·lambda架构·实时数据·离线数据
༺心有谦谦结༻2 年前
淘宝数据分析——Python爬虫模式♥
数据采集·python爬虫·实时数据·电商api·淘宝销量·淘宝店铺·淘宝天猫app
ETLCloud数据集成社区2 年前
ETLCloud:实现数据库快速输入输出的利器
数据库·etl·数据集成·实时数据
ETLCloud数据集成社区2 年前
ETLCloud的应用策略——实时数据处理是关键
etl·数据集成·实时数据