尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
从零搭建实时tick行情监控面板:架构、性能与避坑指南
tick-stock-panel这个项目说白了就是我自己给自己做的“看盘台”。起因很简单有一段时间做短线复盘和日内盯盘需要同时关注几十只票的tick级成交情况但当时用的行情App切来切去实在不方便关键的大单往往就在切换页面的瞬间溜走了。于是我用浏览器搭了一个实时行情面板把关注的股票成交明细、快照、大单异动全部聚合到一个窗口里。这篇文章把这个项目的完整链路写清楚包括数据源选型、后端结构、前端渲染以及实际跑起来之后踩过的几个坑。如果你正需要一个实时行情监控工具或者想了解一下实时数据面板从零到一的搭建思路可以参考这条路线。1. 明知App就能看行情我还是要自建面板的三个理由先说清楚“tick数据”到底是什么。逐笔成交就是交易所撮合成交后产生的一笔一笔交易明细一次成交就是一条tick。和分钟K线不同tick级数据能看到每一笔成交的精确时间、成交价格、成交量、成交额还能看出这一笔是主动买还是主动卖。对于想观察盘中细节的人而言这个粒度是分时图和一分钟K线还原不出来的。市场上有太多现成的看盘软件手机端、PC端都有那我为什么还非得自己折腾一个三个原因第一个原因多标的并行监控的需求现成App满足得不好。手机上的行情软件主界面通常一次只能聚焦一只股票想要看另一只必须退出当前页面再搜索。哪怕很多软件支持自选股列表列表里也只有快照数据比如最新价、涨跌幅、成交量想看某一只的逐笔成交还是得单独点进去。如果我持仓结构比较分散比如同时看十只以上的票靠手工切换页面基本盯不过来。自建面板的核心价值就是把几十只票的明细、快照、预警放在同一屏上扫一眼就有结论。第二个原因默认的视觉逻辑跟我的习惯不匹配。盘中盯逐笔我最关心的是三件事大单、主动买卖方向、价格异动。但普通软件的一列成交记录里这些信息要么被密集的数字淹没了要么需要自己拿眼睛去分辨。自己搭面板就可以重新定义“什么值得被强调”单笔超过一定金额的直接整行高亮主动买的暖色、主动卖的冷色涨停和跌停价附近触碰时用背景色提示。这些东西在自建面板里就是一行CSS的事在现成软件里却很难调整。第三个原因监控结果要能编程化使用。看盘软件做好看不难难的是把行情和脚本、数据库、告警接起来。比如我想在某个票连续出现三笔大单主动买入时触发一个声音提醒或者把每天盘中的关键成交写进本地文件做复盘这些需求现成软件几乎不支持。自己写面板行情流到了自己手里怎么处理都是自由。我做tick-stock-panel的同时顺手就把“大单异动日志”和“成交数据落盘”这两个需求一起实现了。不过我也给自己划了一条明确的边界这个面板只做行情监控和事后复盘不做交易下单。下单涉及券商接口、风控、交易规则和看盘是两套逻辑混在一起只会让代码和心态都变复杂。面板的定位就是“看得更快、更准、更舒服”。2. 行情源选型延迟、字段、限频的一次次实测做这类项目第一个绕不开的问题就是数据从哪来。我前前后后试过几条路各有取舍这里把这轮实测的结论直接放出来。2.1 三类主流行情源的差异第一类是基于HTTP的免费快照接口。这类接口在很多免费数据源里都能碰到一次请求返回若干只股票的最新快照字段一般包括最新价、涨跌幅、成交量、成交额、买卖五档。优点是接入简单不需要维持长连接写个轮询循环就行缺点是延迟通常在1到3秒之间而且绝大多数快照接口不提供逐笔成交只有按分钟聚合的量。逐笔要想拿到得靠另一类通道。第二类是基于WebSocket的实时推送行情。这类通道建立连接后服务器会持续推送最新成交和快照延迟可以做到几百毫秒甚至更低部分通道还提供真正的逐笔数据。缺点是免费通道的选择不多而且连接稳定性、推送字段的完整性都得自己实测验证不能拿文档当保证。第三类是机构级的专业行情终端接口。数据质量确实最好逐笔和十档盘口都有但成本也高个人开发者一般不会作为首选。我自己做的这个项目就不是为高频交易准备的目标是“复盘够用、盯盘够快”所以这一路当时只做了调研没有实际接入。2.2 我最终采用了“WS主力 HTTP兜底”的双通道方案实测下来纯用HTTP轮询的问题是延迟波动大。行情平稳的时候大概一秒拉一次勉强能看行情剧烈波动的时候接口响应变慢等到数据回来盘面已经往前走了好几笔。纯用WebSocket的问题则相反连接偶尔断断的时候如果没有任何兜底面板就白屏了。所以最终方案是让两条通道共存正常运行时以WebSocket推送为主面板上几乎所有tick都走这条路同时保留一个低频的HTTP轮询间隔设成3到5秒只用来拉取自定义股票列表的快照做校准。两者之间用一个内存缓存层做衔接谁的数据先到就用谁的并记录数据到达的本地时间前端可以根据这个时间判断行情新鲜度。这套组合跑下来效果比我预想中稳定。连续盯一个完整交易时段WebSocket断线的次数并不多断线后HTTP兜底能保证面板不处于“瞎”的状态重连成功后又能马上切换回实时通道。对于个人项目来说性价比是够的。选型时还有一个容易忽略的点限频。免费的HTTP快照接口不会要求你每秒只能请求多少次但如果请求频率过高很容易被临时限流表现为突然返回空数据或直接超时。我建议轮询间隔至少控制在1秒以上并且不要一次性请求全市场几千只票只请求自选列表里的几十只压力完全在可控范围内。如果你发现行情源频繁抽风先别怪对方回头看看自己的请求节奏是不是太快了。2.3 统一内部Tick格式屏蔽不同源的数据差异不同行情源返回的字段名、单位、时间格式都不一样。为了不让上层代码跟着数据源走我在后端定义了一个统一的内部Tick对象symbol证券代码统一带上交易所前缀例如“SH600000”ts成交时间精确到毫秒price成交价格统一用浮点数volume成交量统一用“股”作为单位amount成交金额统一用“元”side主动方向B代表主动买S代表主动卖T代表中性行情接入层拿到原始数据后第一件事就是转成这个结构。后续无论是写日志、做预警还是推给前端都只认这个标准格式。这个抽象层虽然多写了一点代码但好处是换数据源时完全不需要动核心逻辑直接换适配器就行。3. 后端管道从秒级轮询改成实时推送的架构变化后端我最初写得很糙一个Python脚本每秒请求一次接口然后往前端输出JSON。当时想的很简单能显示就行。结果数据量稍微上来就发现不对劲页面要主动反复刷新体验很差而且轮询方式下前端并不知道数据什么时候会变化只能盲目地拉浪费带宽和计算。3.1 三层结构接入层、分发层、查询层后来重构成三个模块接入层负责对接各个行情源每个行情源一个适配器。适配器只做一件事把原始数据解析成内部Tick对象然后交给核心层。HTTP轮询适配器内部是一个asyncio循环按照配置好的间隔不断拉快照WebSocket适配器内部是一个长连接循环收到推送消息后逐条解析。分发层是核心维护一张订阅表记录每只股票和当前所有前端连接的关系。每当接入层传来一条新的Tick分发层做三件事更新该股票的最新快照缓存、推送给所有订阅了这只股票的前端连接、写入本地日志如果开启了落盘。分发层还负责心跳检查定期清理失活的连接。查询层面向外部提供两类入口一是REST接口用于前端打开页面时拉一次全量快照二是WebSocket入口用于后续所有实时增量推送。为什么选asyncio而不是多线程因为场景几乎是纯IO密集型的。行情推送、前端连接、数据库写入没有CPU密集计算用协程完全够。实测下来同时维护几十个前端连接和处理上千条tick的推送进程内存只占几十到一两百MBCPU占用基本可以忽略。3.2 订阅与分发的核心代码骨架这里分享的是最精简的订阅分发逻辑其他细节比如日志、预警都是挂在同样的事件回调上。# core/bus.py —— 一个极简的Tick分发总线 import asyncio from collections import defaultdict class TickBus: def __init__(self): self.clients defaultdict(set) # symbol - list[websocket] self.cache {} # symbol - latest tick dict async def subscribe(self, ws, symbols): for sym in symbols: self.clients[sym].add(ws) # 订阅成功后立即把缓存快照推过去 if sym in self.cache: await ws.send_json({snapshot: self.cache[sym]}) def unsubscribe(self, ws, symbols): for sym in symbols: self.clients[sym].discard(ws) async def publish(self, tick): # 更新缓存 self.cache[tick[symbol]] tick # 推送给所有订阅该标的的连接 for ws in list(self.clients.get(tick[symbol], ())): try: await ws.send_json({tick: tick}) except Exception: # 连接已经失效交给外层统一清理 await self.unsubscribe(ws, [tick[symbol]])这里有一个很容易踩的细节ws.send_json如果连接断开会抛异常但异常处理不能只在这里做因为并发场景下同一批连接可能同时在分发层和清理协程里被操作。我的做法是分发层只做推送尝试真正的清理统一由一个巡检协程兜底避免在发送过程中反复删改订阅表导致状态不一致。3.3 数据落盘与回放的基础设计逐笔数据转瞬即逝但复盘时又特别希望“时光倒流”。我给面板加了一个可选的落盘功能订阅里开会把每一条Tick写入SQLite表结构就是内部Tick字段加一个本地接收时间。因为是纯追加写速度很快实测一个交易时段下来单标的几千条记录整个数据库文件不到几MB。落盘时不需要保证100%不丢失盘后写个脚本把当天数据汇总成CSV就够了。回放功能的实现也偷了点巧把落盘数据读出来用定时器按原始时间间隔逐条重新推给前端。前端完全感知不到“这是历史数据”还是“实时数据”只是速度慢了一些。这样复盘时就能用同一套界面回看当天盘中每一个特殊节点。3.4 背压和数据过期策略WebSocket推送出去时如果某个前端连接因为网络原因迟迟收不到消息消息队列会越积越多。我的处理是队列只保留最近100条超过的部分直接丢弃。前端断线重连后会先通过REST接口拉一次全量快照把当前价格和最新成交一次性补回来不会因为丢失几秒的tick而产生数据空洞。丢弃老数据的思路在做行情系统时很重要。行情数据是强时效性的晚到的一秒数据价值基本为零。与其让积压数据把内存和带宽拖垮不如让前端快速重新对齐。这里的取舍和“丢消息会出错”的直觉相反但实时系统里“及时一致性”优先于“最终一致性”。4. 前端排版让逐笔成交信息一眼能读而不是数字堆在屏幕上后端把数据管好后真正决定这个面板好不好用的其实是前端。我的核心诉求只有一个长时间盯盘不累扫一眼就能知道当前市场在发生什么。页面设计围绕这个目标拆成了几个模块。4.1 面板布局与信息层级顶部是一个全局状态栏显示当前时间、WebSocket连接状态、最后一条数据到达时间、总延迟毫秒数。只要看到“最后数据到达时间”超过3秒第一反应就是去看连接状态而不是怀疑数据出了问题。左侧是自选股列表每一行显示代码、名称、最新价、涨跌幅、当前成交量。点击任意一行中间主区域就会切换到该标的的详情视图。这里的列表和中间详情区是联动关系切换标的时前端会先向REST接口发一个快照请求然后转成WS订阅保证切换后马上有数据。中间主区域分上下两块。上方是1分钟K线图用tick数据本地聚合出来的K线比外部接口的分时数据更灵活下方是逐笔成交滚动表格这是整个面板信息密度最高的地方。右侧是一个事件日志区预警信息和手动标记的大单都会在这里滚动显示。4.2 颜色、高亮与信息密度控制颜色按A股习惯红涨绿跌。tick的方向字段直接影响数据的呈现主动买用暖色主动卖用冷色这笔成交是主力主动吃卖单还是主动性抛售一眼就能区分。大单高亮是我最看重的功能。我在前端定义一个“大单阈值”参数比如单笔成交金额超过50万元就算大单。超过阈值的tick整行加粗并用更亮的背景色标记超过100万元的再上一档用更强的底色和加粗字体。加入这个规则后盯盘时注意力就可以集中在真正重要的成交上而不是被密密麻麻的小单淹没。价格异动也有对应的视觉反馈最新价突破当日最高价、触及涨停价、或者在极短时间内连续跳动超过设定幅度时对应区域会有一次闪烁。闪烁用CSS动画实现持续0.5秒后消失不会造成持续干扰。关于信息密度我的建议是宁少勿多。逐笔成交表每一行只显示四列时间、价格、成交量、方向。单位统一数字右对齐不需要的列一律不展示。当窗口中可以容纳的信息变少大脑的负担反而会降下来。很多人做监控面板的误区是想把所有数据都塞进去结果什么都看不清。4.3 渲染性能为什么不能用一条条DOM堆积面板的逐笔成交更新频率在行情活跃时每秒可能来几十条甚至上百条。如果每来一条都直接操作一次DOM用不了多久页面就会卡顿。我踩过这个坑最初直接用一个数组往表格里append新行大约跑到几千条之后滑动和输入都已经明显掉帧。后来的解法是批量更新加虚拟滚动。WebSocket收到消息后先把tick推进一个待处理数组下一次浏览器requestAnimationFrame回调时一次性把所有待处理数据追加到表格里。这样渲染频率和刷新率对齐避免了每条数据都触发一次重排。同时表格只渲染可视区域内的行数据总量哪怕有上万行DOM数量始终保持在几十到几百个。// 批量追加tick的示意 let pendingTicks []; let schedulerRunning false; socket.onmessage (e) { const data JSON.parse(e.data); if (data.tick) { pendingTicks.push(data.tick); } if (!schedulerRunning) { schedulerRunning true; requestAnimationFrame(() { appendTicks(pendingTicks); pendingTicks []; schedulerRunning false; }); } };这个优化看起来很小但对长时间运行的页面影响非常大。优化完成后即使持续跑一整个交易日内存也不会因为DOM数量膨胀而失控。5. 上线两天就暴露的四个坑以及现在的处理方式任何实时系统都要面对同一个残忍事实上线前你觉得一切完美上线后所有隐藏问题开始排队出现。面板跑了两天我先后遇到下面四个问题。5.1 连接断流后的重连风暴第一次遇到WebSocket断开时我没想太多直接让前端自动重连。结果服务器端恢复后前端十几个页面同时发起重连请求又被行情源当成突发访问限制了。这就是重连风暴所有客户端在同一时间点涌向服务端本来是为了恢复可用性结果反而把服务压垮。解决方案是指数退避加随机抖动。每次重连前等待的时间按照“1秒、2秒、4秒、8秒……”递增并且加入一个0到1秒的随机偏移避免所有连接排在同一时刻重连。同时在后端设置一个“断线护栏”如果重连失败超过五次就进入冷静期只保留HTTP兜底轮询不再尝试建立WebSocket连接。5.2 时间戳错乱跨日和延迟数据这个问题一开始让我排查了好久。行情数据里的时间戳有些源给的是无时区时间有些给的是毫秒级Unix时间有的干脆只有时分秒没有日期。盘中看着还好但跨过午盘休市或者隔夜后数据排序就乱了。后续统一了两套时间一是数据源自带的成交时间二是本地接收到消息的时间两者都记录到Tick对象里。前端排序一律用本地接收时间成交时间只作为展示字段。当“本地接收时间”和“成交时间”的差值超过5秒时前端会在该条数据上打一个“延迟”标记显示为灰色。这样即使数据源本身有点问题面板上也不会出现时间倒退或数据跳变的现象。5.3 停牌、除权除息、新股的边界状态停牌的票没有成交这是最简单的边界。复杂的是除权除息和上市首日这两个场景。除权除息当天股价开盘价会有一个向下跳空如果还按照前一天的收盘价算涨跌幅数据就是错的。上市首日就更特殊了没有昨收基准涨跌幅计算规则和正常交易日完全不同。我的处理方式是维护一个“基本面状态”字段正常、停牌、除权日、上市首日。状态由每日盘前通过快照接口预取一次盘中再根据快照字段做二次修正。前端展示涨跌幅时会根据这个状态选择对应的基准价。这个小状态字段看起来不起眼但极大地避免了复盘时被假信号误导。5.4 浏览器后台标签页的节流陷阱面板如果在电脑上开成一个小窗口顺手去干别的事把标签切到后台浏览器会定时器节流setInterval和requestAnimationFrame在后台都被拉长到一秒一次甚至更长。这个对纯前端渲染影响不大但会影响预警提示的及时性明明触发了一个异动提醒声音却延迟了好几秒。解决思路是把预警判断放到后端而不是依赖前端定时器。后端自己就能扫Tick流是否符合预警条件符合就直接推送一条alert事件到前端。前端只负责接收和展示不负责判断。这样即使标签页在后台WebSocket消息依然会正常到达声音提醒也能准点触发。6. 下一步预警、复盘、多市场这个面板还能继续长大目前面板已经能稳定完成实时监控和复盘回放。我自己最想继续做的几个方向也顺手说一下每个方向都可以独立扩展不用推翻重来。预警引擎是优先级最高的。现在我只做了一个简单的大单金额阈值判断后续想扩展成可配置规则比如“连续三笔大单主动买”“5分钟内涨幅超过2%”“跌破当日开盘价后快速拉起”这类条件。规则用Python脚本描述挂在分发层的事件回调和数据聚合器上命中后推给前端并触发声音提醒。声音直接用Web Audio API生成一个短促的提示音不用额外引音频文件浏览器兼容性也好。复盘回放的数据层已经就绪剩下的是交互打磨。比如增加回放速度调节、按时间区间跳转、在K线图上标记大单位置。这块做完之后盘后研究就会比盘中盯盘更高效。多市场支持是我留给未来的扩展点。A股、港股、美股、期货、数字货币的交易规则和行情粒度差异很大不能用一个适配器硬撑。内部已经在Tick对象的symbol字段上加了市场前缀后续每个市场写一个独立适配器接入层多注册一个源就行。不同市场的颜色习惯也要做成配置项比如美股是绿涨红跌不能和A股混用。最后再分享一个我实际用下来的心得做这类实时数据面板最重要的不是功能多而是“刷新链路短”和“状态可感知”。链路短指的是从行情源到眼睛之间的距离要尽可能短中间不要有多余的轮询、缓存、重复渲染状态可感知指的是任何时候都得知道面板的行情是新鲜的还是过期的、连接是正常的还是断开的。这两点做到了即使界面上只有几列最简单的数字这个面板也是好用的。tick-stock-panel这个名字里的“panel”对我来说就是一个随时能瞄一眼、能信任的实时窗口。
RELATED

相关推荐

鸿蒙系统能Ghost吗?跨设备移植与系统调优边界解析

鸿蒙系统能Ghost吗?跨设备移植与系统调优边界解析

刷机圈子里有个老问题隔三差五就被翻出来:鸿蒙系统能 ghost 吗?能不能把别的机器换成鸿蒙系统?鸿蒙系统怎么改才能顺手一点?这三个问题看着像一回事,其实是三个完全不同的技术层面——一个是 Windows 时代的磁盘镜像思…

📅 2026/11/3 18:00:24
Qt流程图编辑器设计:QGraphicsView与QUndoStack核心实践

Qt流程图编辑器设计:QGraphicsView与QUndoStack核心实践

简介:面向具备一定C基础的Qt桌面开发者,这份基于Qt的流程图编辑器完整工程源码,系统演示了自定义图形组件、拖拽交互、连线自动吸附、序列化保存与撤销重做等核心功能的实现思路。工程以QGraphicsView / QGraphicsItem框架为基础,…

📅 2026/11/3 18:00:24
Ghost为何装不了鸿蒙?镜像克隆、升级回退与开发板烧录全解析

Ghost为何装不了鸿蒙?镜像克隆、升级回退与开发板烧录全解析

Ghost 这个词在装机圈里流传了二十多年,几乎成了一切"一键还原系统"的代名词。所以当鸿蒙系统铺开之后,后台收到的提问里出现频率最高的一类就是:"鸿蒙系统能 ghost 吗""能不能给我的手机换成鸿蒙""鸿蒙系…

📅 2026/11/3 18:00:24
MORE NEWS

更多资讯

📰

产业互联网投资逻辑与核心赛道解析

1. 产业互联网的价值投资逻辑产业互联网的本质是通过数字化技术重构传统产业链条,实现供需精准匹配和资源高效配置。与消费互联网不同,产业互联网具有三个显著特征:一是服务对象从C端转向B端,二是价值创造方式从流量变现转向效率提…

📰

CS2更新后掉帧卡顿闪退:一小时定位根因排查法

上周更新完之后,我照常开了一局,热身的时候还好好的,进正赛第二回合开始每隔五六秒就明显一顿,帧率监控上的数字从两百多直接砸到四十,鼠标划过去准星跟不上手。第三天更离谱,进图加载到 90% 直接闪退&…

📰

ICLR 2026论文揭示AI研究的未解难题与实战经验

1. 从ICLR 2026入选论文看AI研究的未竟之地当李飞飞团队的6篇论文入选ICLR 2026的消息传来时,整个AI社区都在讨论这些突破性成果。但作为一名在计算机视觉领域摸爬滚打十年的研究者,我更想聊聊那些论文里没写出来的故事——那些"还没做到"的部…

📰

Univer SDK:可编程文档能力的开源技术底座

1. 这不是又一个“在线文档”套壳项目:Univer 是什么,它解决的到底是什么问题? Univer 这个名字最近在前端技术圈、企业级应用开发和低代码平台建设者中频繁出现,但它绝不是另一个“国产版 Google Docs”或“网页版 Excel”的营销…

📰

前端模块系统兼容性问题解决方案

1. 问题现象与背景解析最近在将一个TypeScript项目打包部署到生产环境时,控制台突然抛出"Uncaught ReferenceError: require is not defined"错误。这个报错在Node.js环境下运行正常,但一到浏览器环境就崩溃。经过排查发现,这是前端…

📰

大模型与推荐系统融合:QARM V2架构解析与实践

1. 项目概述:当推荐系统遇上大模型推荐系统早已成为互联网产品的标配功能,但传统协同过滤算法在面对短视频平台的海量内容时,就像用渔网捞金鱼——看似热闹却难以精准捕获用户兴趣。快手QARM V2的突破在于将大语言模型的语义理解能力与传统推…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬