先讲一个真实场景。之前帮朋友排查一个实盘策略的异常回撤,他的策略很简单:突破20周期高点就做多,跌破20周期低点就止损。模拟盘跑了三个月,收益曲线很漂亮,上了实盘之后第一个星期,账户连续触发止损,每一笔都亏在“最低点”。一开始我们都以为是策略失效了,后来把行情日志和下单日志放到同一个时间轴上一看,问题根本不在策略,而在他接的行情源:盘中行情偶尔会瞬间“倒带”,也就是先收到一个新价格,下一秒又收到一条时间戳更早的旧数据。他的策略是典型的价格突破逻辑,这种乱序数据一进来,突破和跌破的信号就会在几毫秒内反转,止损单当场就被打穿。
这个例子很能说明问题。很多人做量化选行情API的时候,目光只盯着一件事:延迟够不够低。延迟低当然重要,但“实时行情”四个字背后的坑,远远不止延迟这一项。数据是否连续、断线后能不能正确恢复、时间戳是否可靠、盘口快照和增量怎么拼接,这些才决定了你的行情链路能不能真正扛住实盘。这篇文章会把选型时该看的指标、数据质量暗坑、接入后的工程故障、以及不同资金规模下的方案都过一遍,希望能帮你少交一点市场学费。
1. “实时”两个字有多少水分:从交易所撮合到策略回调的完整链路
1.1 行情从产生到你手里,中间经过了至少七个环节
很多人觉得“实时行情”就是打开API就能收到当下价格,其实一条行情从交易所撮合引擎产生,到你策略代码里的回调函数被触发,中间隔着一条很长的链路。
笼统拆一下,大概包括这几个环节:交易所撮合产生行情事件,写入行情发布系统;行情网关做编码和组播广播;你的行情服务商通过专线或公网接收,做解析和二次转发;数据到达你的接入服务器之后,还要经过协议层解码、时间戳对齐、本地队列缓冲;最后才轮到策略逻辑从队列里取数据、计算信号。
这里每一跳都是有延迟的。交易所核心直通行情的典型延迟能做到个位数毫秒到二十毫秒级别;第三方聚合转发平台一般在50到200毫秒;如果用的是REST接口轮询,哪怕每秒拉一次,“实时”也已经是秒级了。也就是说,“实时”从来不是一个绝对概念,而是一个“距离产生位次”的相对概念。不同的服务商宣称的“实时”,实际的物理含义可以差两个数量级。
这里还要区分两种时间戳。一种是交易所事件时间,也就是这笔行情在交易所撮合那一刻的时间;另一种是服务商到达时间,也就是你的机器收到数据那一刻的时间。很多第三方API默认给的是到达时间,而不是交易所时间。如果你拿到达时间去算均线、算波动率、切K线,等于给所有行情都叠加了一个不可控且随机变化的偏移。这个问题平时不明显,一旦发生网络抖动或者数据源主备切换,策略的时间轴就会乱掉。
所以,选行情API时第一个要确认的问题不是“延迟多少毫秒”,而是“你们的行情自带交易所事件时间戳吗”。没有事件时间戳的数据流,再快也只是一个好看的幌子。
1.2 快照推送和逐笔推送,“实时”的含义完全不同
“实时推送”这四个字也很有迷惑性。市面上的行情推送大体分两种模式:快照推送和逐笔推送。
快照推送是按固定时间间隔(比如3秒)推一帧当前盘口状态,包括最新价、成交量、买卖五档或十档。这种模式下,“实时”其实是3秒一帧的幻灯片,中间这段发生了什么,你完全不知道。对分钟级、秒级的趋势策略来说问题不大,但如果你要做盘口微观结构分析,比如看买卖盘口失衡、追踪大单动向,快照模式的信息量就远远不够了。
逐笔推送则不同,每一笔成交、每一条委托变动都会实时推给你,数据量要大好几个量级,解析复杂度也更高。Level-2行情通常就是逐笔级别的。
选型前先想清楚自己的策略到底在吃哪种数据。做日线趋势的,用3秒快照绰绰有余;做高频抢单的,别说快照,普通逐笔都可能不够,得用交易所内部更细腻的数据。最怕的情况是,你明明只需要快照,却按逐笔的价钱买了全套;反过来,策略依赖逐笔成交明细,结果买的是“伪逐笔”——本质上还是快照轮询,只是在快照之间做了插值。这种坑在检查文档时就要问清楚:你们推的是逐笔成交、逐笔委托,还是周期快照?
1.3 行情停止更新,未必是市场没有成交
还有一个经常被忽略的细节:盘中某些标的可能长时间没有行情推送。这时候你的策略面对两种可能:一是这个标的正处于无成交时段,比如刚开盘的冷门股;二是你的数据源断流了。
如果没有心跳检测和最后更新时间检查,策略很容易把“数据源断了”误判成“市场很安静”。尤其在做止损策略时,假如某个持仓标的因为数据源断流而不再推送行情,策略可能一直拿着一个过期的旧价格,等数据恢复的一瞬间才补做判断,这时候价格可能已经跑了很远。
我现在的做法是,所有行情客户端必须加一层stale监控:超过N秒没收到某个标的的任何推送,就触发告警,并且禁止策略继续以旧价格计算信号,直到新的有效行情到达。这个N要根据标的的流动性来定,大体上,成交活跃的品种超过3到5秒没推送就该警惕,冷门品种可以放宽一些。
这其实就是“有实时数据”和“实盘够用”之间的一个重要差别:前者只是一个瞬时状态,后者要求的是持续、可验证、带心跳的数据流。
2. 选型先看四个硬指标,别把“延迟低”当成唯一标准
2.1 延迟不是越低越好,而是匹配你的策略节奏
“这个行情源延迟只有5毫秒,选它准没错”,这是我见过最多的选型思路。但延迟这个指标,一定要放在策略节奏里去判断。
可以按策略对延迟的敏感度粗略分三类。第一类是毫秒级高频策略,比如抢单、盘口瞬时捕捉,这种只能放在交易所机房附近的专用环境里跑,普通云服务器加第三方行情源基本不用想。第二类是秒级或亚秒级的中频策略,比如日内动量、事件驱动、短周期突破,这种对延迟的要求通常在一两百毫秒以内,一个稳定的WebSocket推送通道就能满足。第三类是分钟级、小时级、日线级的低频策略,延迟在两三秒甚至更慢都无所谓的,关键是数据完整性和复权准确性。
大部分个人开发者和中小机构其实属于后两类。盲目追逐毫秒级延迟,不仅要多花钱,还可能引入稳定性更差的高性能通道。更要紧的是,行情延迟只是整个链路的一部分,你的交易执行还有柜台、风控、网络、交易所回报等环节,哪怕行情快了20毫秒,下单那一路如果慢了100毫秒,整体依然是慢的。
我有一个习惯:先算“全链路延迟预算”,从行情数据到达本地,到策略发出信号,再到订单到达交易所并被确认,这是一条完整的链路。延迟预算平摊到每一段之后,你会发现行情源其实不是最大的瓶颈。把预算花在执行链路上,往往性价比更高。
2.2 Level-1和Level-2,中间隔的不只是价格差距
经常有人问,要不要直接上Level-2行情?我的回答通常是反问一句:你的策略用得上盘口微观特征吗?
Level-1行情提供的是最新价、成交量、买卖盘口等基础快照,对绝大多数趋势跟踪、均线突破、通道突破策略来说已经足够。Level-2行情则能提供逐笔成交、逐笔委托、完整盘口深度,可以计算盘口不平衡、撤单率、大单资金流、冰山单痕迹等微观特征,但这些能力也意味着更高的费用、更大的数据量、更复杂的解析逻辑。
举个例子,一份逐笔委托数据里,大量委托可能在到达后几秒就被撤掉了。如果你没有把撤单事件和原始委托关联起来,资金流向的计算结果会严重失真。这不是数据源的锅,而是你的解析逻辑配不上这个数据粒度。很多团队花大价钱接了Level-2,最后算出来的指标还不如用Level-1快照算得干净。
建议是:先在Level-1上把策略逻辑跑通,确认收益瓶颈真的来自“信息粒度不足”,再考虑升级Level-2。否则很容易陷入“数据升级了,策略反而更差了”的尴尬局面。
2.3 REST轮询与WebSocket推送,正确用法是组合
REST接口和WebSocket不是二选一的关系,而是分工关系。
REST适合做低频操作:盘前初始化时拉取日线、分钟线历史数据,盘后做数据补拉,或者在WebSocket断线后补拉一段缺失行情。轮询模式下,哪怕你用再高的频率去拉,也拿不到真正逐笔级别的实时数据,而且高频轮询很容易触发服务商限流。
WebSocket适合做盘中主链路。服务端主动推送,延迟稳定,数据到达的节奏也更接近真实行情节奏。但WebSocket接入要确认几个细节:支持多少路并发订阅,单连接最多订阅多少个标的,心跳包怎么发、多久没收到pong会被判定掉线,断线之后服务端支不支持断点续传,还是需要客户端主动补拉快照。
我见过一个挺典型的坑:有人在开盘瞬间用程序一次性订阅了大量标的,触发了服务商的订阅配额限制,连接被静默断开。断线重连后他又重新订阅一遍,结果再次触发限流,陷入“断开-订阅-再断开”的循环。正确做法是开盘前完成全部订阅,盘中尽量不做增删订阅操作,真需要临时加标的,也要控制节奏、分批订阅。
2.4 免费源与付费源,差的不只是“要不要钱”
免费行情源在社区里很受欢迎,用来学API、跑回测、看盘完全没问题。但把它作为实盘主链路,要慎重。
免费源的典型问题有几个:稳定性没有承诺,开盘高峰时段偶尔断流;数据完整性没有保证,缺tick、丢行情是常事;时间戳精度可能不够,有的只有秒级时间戳;在线率和服务响应全凭维护者个人精力,一旦项目停更,你的行情链路就断了。
付费源也不是越贵越好。真正要看的指标是:服务商有没有明确的服务等级承诺,断线重连和数据补拉机制是否成熟,是否有技术支持响应,是否提供历史数据补拉能力。
我在实际项目中比较偏好这样一套组合:主力实盘账户用一个数据质量扎实的商业行情源,同时保留一个免费源作为独立的心跳对比链,只用它来验证主源数据是否异常变化,不参与下单。这样既控制成本,又不把全部信任押在单一服务商身上。
3. 数据质量的三个暗坑:乱序、断线重连、异常值
3.1 乱序到达:你的策略可能在“穿越时间”下单
行情乱序,是实盘里最隐蔽也最致命的坑之一。
乱序的产生原因很多。行情服务商为了追求高可用,往往采用多机房或多实例同时对外推送,不同实例之间延迟不同,在网络上就可能产生乱序;当服务商做主备切换时,新链路补发旧数据,也会造成旧事件晚到;如果策略同时订阅了多个数据源做交叉验证,不同来源的同一笔成交更可能先后到达。
乱序会造成什么后果?回到文章开头那个例子,策略先收到一个新价格,随后又收到一条时间戳更早的旧数据,突破信号和跌破信号在极短时间内相继触发,止损单、反向单全都打在错误的价位上。这种错误在日志里看起来就像策略发了疯,实际上只是数据时间线乱了。
排查乱序的过程,我当时是这样做的:
第一,把行情日志完整打出来,重点记录每条数据的来源、到达时间、事件时间、序列号。第二,按事件时间重新排序,计算“延迟到达”的数据比例和最大延迟量。第三,直接问服务商要答案,确认他们的推送架构是否存在多路并发和主备机制。第四,本地加一个排序窗口:维护一个按事件时间排序的定长缓冲,迟到的旧数据超过窗口范围直接丢弃,不参与信号计算。
这里有一个原则值得反复强调:对于下单决策,只认单一权威源,次级源只用来参考和校验。一旦策略里混用了两套独立来源的数据做实时下单判断,乱序问题几乎是必然出现的。
3.2 断线重连:连接恢复不等于数据恢复
行情WebSocket一旦断开,重连成功只代表“通道通了”,不代表你本地的盘口状态是对的。行情协议通常采用“快照+增量”机制,快照是全量盘口状态,增量是后续每笔变动。断线期间,你错过了一串增量,如果重连后直接继续接收增量而不先取一次全量快照,本地盘口会从错误的基础上累加,买一卖一价格可能出现倒挂,盘口深度可能完全错乱。
我早年踩过一个具体的坑。某次盘中网络抖动,行情连接断了大概3秒,自动重连成功后,盘口五档数据全部乱掉了。策略很快发现“买一竟然高于卖一”,于是判定行情异常,紧急平掉了一批仓位。事后一查,问题不出在策略,而是客户端在重连后没有先请求全量快照,直接拿旧快照叠增量,数据自然就是错的。
后来我把重连流程彻底改成了这样一步都不能少的顺序:
首先,检测到断线后立即标记本地状态为“不可用”,策略层禁止使用旧数据计算信号。其次,重连成功后,先请求一次全量快照,拿到快照后清空本地盘口状态,再开始接收增量。接着,对每条增量消息做序列号连续性检测,发现有跳号说明中间仍然有缺失,那就再次请求全量快照,直到序列号连续为止。最后,收到新的有效快照和连续增量之后,才把状态恢复成“可用”,允许策略继续计算。
这个过程说起来简单,但很多API文档不会告诉你,需要客户端自己实现。文档里那句“重连后会自动补齐数据”,补的可能是增量,而不是全量快照,千万别想当然。
3.3 异常价格与除权除息:行情全对,策略也会被“假信号”坑
就算行情没有乱序、没有断线,数据本身也可能包含“合法但不合理”的值。
比如某些行情源在特殊时点会推出一条明显错误的价格,真实价格明明是5000,突然推过来一个300,过几秒又恢复正常。策略拿到300之后,按价格止损逻辑计算,可能瞬间砍在最低点,然后后悔一整年。再比如盘中停牌、复牌的瞬间,最新价会出现跳跃;涨跌停板附近,盘口数据也可能呈现极端状态;这些都不算数据源“错”,但不能直接喂给策略。
除权除息更是典型。一只股票10送10,除权日当天价格直接“腰斩”,没做复权的策略会把这次除权当成一根巨大阴线,价格类止损线全线错乱。做量化的人手里一定要有一套完整的复权因子数据,入场之前就要把历史数据切成前复权或后复权口径,实盘行情也要同步做事件化处理。
我在数据接入层统一的处理思路是三层过滤。第一层做字段合法性校验,价格、成交量、时间戳这些字段缺失或为负直接丢弃。第二层做价格合理性校验,当前价格偏离近期均价多个标准差时标记为可疑数据,不参与信号计算。第三层做事件化校验,把除权除息、停复牌这类事件与行情关联起来,在事件点附近自动调整策略状态。
但这里要提醒一句,过滤是一把双刃剑。过滤太激进,会把真实的大波动误杀,尤其趋势策略最怕在真正突破的瞬间被“合理化过滤”掉信号。过滤的目的不是追求数据绝对干净,而是处理“明显异常”的数据,取舍上要结合策略类型去调阈值。
4. 行情到位之后,工程链路依然会击穿你
4.1 全市场订阅时,带宽和存储的账要提前算
“我有全市场行情”听起来很厉害,但代价是带宽、内存和存储。
可以简单算一笔账。假如做A股全市场Level-2逐笔,沪深两市在活跃时段可能每分钟产生数万笔到数十万笔的委托和成交事件。每一笔二进制消息按64字节算,持续传输的流量就能达到每秒几十MB,换算成带宽就是几百Mbps。如果数据源提供的是JSON文本格式,体积还要翻好几倍。普通云主机几Mbps到几十Mbps的带宽,在这种流量前面基本就是被瞬间打满。
就算带宽够,存储也是问题。全市场Level-2一天的原始数据轻松上GB甚至更大,连续存一个月就是几十GB到几百GB,数据库的写入压力也很可观。
所以,做个人量化或者中小规模团队,我不建议一上来就全市场订阅。更务实的方案是按需订阅,只订阅策略真正关注的标的和自选池,把全市场数据降级为分钟级快照或日线级汇总。行情接入之后,还要分热数据和冷数据,热数据放内存或者Redis,分钟线、日线这类冷数据垂直拆表落库,这样成本和查询效率都可控。
4.2 在行情回调里干重活,是新手最容易犯的性能错误
这个问题我几乎在每个接入行情API的项目里都见过:有人在行情回调函数里直接写数据库,有人直接在回调里做HTTP请求,有人在回调里打印大段日志。这些都是致命的。
行情API底层通常是一个事件循环或单线程推送模型,回调函数执行多久,下一批行情就得等多久。你在回调里写一次数据库可能花几十毫秒,这段时间里新到的行情全部被堵在缓冲区,积压越来越多,延迟就会从几毫秒飙升到几秒。最恐怖的是,日志文件被大量写入拖慢磁盘IO,整个进程的响应都会变慢,等到你发现问题时,策略已经基于严重延迟的旧数据做了好几轮判断。
正确架构很简单:回调函数只负责收数据、入内存队列,然后立刻返回。真正做数据处理、信号计算、下单执行的逻辑放在独立的消费者线程或进程里,从队列里按自己的节奏取数据。用Go写就是channel send,用Python写就是queue.put,核心心法是“回调只做收和放,不做算和写”。
4.3 从行情到成交,中间还有一整个执行链路
行情到位之后,实盘的另一半才开始:交易执行。
你的订单从发出到交易所确认,中间要经过交易API、风控、资金校验、券商柜台,每一步都有延迟。很多情况下,滑点不是市场造成的,而是执行链路过长。比如行情显示买一挂单充足,你下市价单准备吃掉买一,可能因为订单路径多了几十毫秒,到交易所时买一已经没了,成交价滑出去好几个tick。
订单状态机是整个执行链路的灵魂。订单的已提交、已确认、部分成交、完全成交、已撤单、拒单这些状态,必须在一个可靠的状态机里流转。部分成交尤其容易出错,如果策略收到“订单已提交”就认为成交完成,后面交易所陆续回来的多条成交回报,就可能被当成重复回报忽略掉,导致持仓数量记录错误。
所以做量化实盘,光盯着行情API是不够的。交易API和行情API最好能统一评估:它们是否来自同一家服务商,是否能复用连接,沙盘和生产环境的切换流程是否顺畅,历史订单查询、当日成交查询这些基础能力是否完善。行情是输入,执行才是输出,两口子都得稳,生活才能过下去。
5. 不同资金规模和策略类型,行情选型的参考方案
5.1 小资金个人开发者:先跑通,再抠延迟
如果你刚把策略从回测搬到模拟盘,资金量不大,策略周期偏分钟级或日线级,那选型的第一原则是“先跑通”。
这个阶段,行情数据的完整性和稳定性比延迟重要得多。哪怕延迟多几十毫秒,日线策略根本感知不到;但如果数据缺了一段,或者断线后没有补齐,策略的持仓状态就可能出现偏差,这才是要命的。
适合这个阶段的是券商官方API配套的行情,或者成熟第三方平台的WebSocket标准行情。看文档是否清晰,接入是否简单,是否有活跃的社区支持,以及断线重连机制是否成熟。不用为了那点延迟去折腾所谓的高性能通道,先确保整套链路从行情到交易都能稳住,资金量爬上去之后再谈优化。
5.2 中大型团队:主备行情源加网关架构
资金量上来了、日内交易频率也上来了,就要开始认真对待“可用性”。
这个阶段我建议至少两个行情源同时接入,一主一备。主源选数据质量最有保障的商业服务,备源可以是另一家不同架构的行情源,平时只做数据一致性校验,比如每个周期比较一次最新价,发现主备价格偏差过大就触发警告。一旦主源连续断流或者数据质量异常,备源能接管行情供应,保证策略不裸奔。
架构上,最好搭一个行情网关服务,把不同API的行情统一解析成内部标准格式,再做订阅分发和缓存。这样好处很多,下游策略不用关心上游换了哪家服务商,组件足够独立,单一数据源挂了,网关层可以做切换而不需要策略改动。同时网关层还能统一做数据清洗、去重、排序、异常过滤,把脏数据挡在策略外面。
5.3 成本与合规边界:别为省小钱买大风险
量化领域有个容易被忽视的问题:行情数据的授权范围。
有些数据源明确禁止将行情二次转发给第三方,也就是说,你把订阅来的行情封装成API再提供给其他人使用,这在很多服务商那里都是违约行为。还有的数据源仅供个人学习研究使用,不能用于实盘交易。用之前一定要把授权协议看清楚,免费源尤其要仔细看,别因为“方便”给自己埋雷。
成本也要算清楚。个人开发者的低成本方案是第三方WebSocket行情加个人交易接口,按需订阅几十只标的,一年费用通常在可接受范围内。专业团队如果要做全市场Level-2、逐笔数据、历史数据全套,费用就会上一个大台阶,这还没算存储和带宽的投入。选型时我会拉一个五年成本表格,把行情订阅费、带宽费、存储费、开发维护工时全部算进去,再看这个方案对应的实盘收益有没有意义。
6. 接入行情源之后的第一周,建议你这样验收
6.1 四件必须做的事,把坑都提前踩一遍
行情API接入完成,不代表可以直接上实盘。我的习惯是留出至少一周的验收时间,做四件事。
第一,连续记录连接质量。每一天几点断线、重连耗时多久、重连后有没有触发全量快照补齐、补拉的数据是否完整,全部记下来。一周之后看统计,如果断线次数过多或者重连后经常补不出完整数据,这个源就要慎重。
第二,做一次人为断网测试。模拟网络抖动,拔掉网线几十秒再恢复,观察客户端是否能按预定义流程恢复快照加增量,策略状态是否能回到正确位置。这一步能暴露你之前所有想当然的假设。
第三,两个行情源并排对比跑一天。找一个主源之外的行情源,同一标的、同一时刻,比较最新价和五档盘口。坚持一整天你会发现,很多你以为的“实时价格”,不同源之间其实对不上。这种差异不是谁错,而是延迟和数据粒度不同,但你必须心里有数。
第四,录制盘中行情再做回放。把一天的实时行情录制下来,用历史回放模式重跑策略,对比实时跑出来的信号和回放跑出来的是否一致。如果不一致,说明你的策略对行情时序敏感,很可能存在数据到达顺序相关的隐藏Bug,务必在实盘前解决。
6.2 我给自己的验收检查清单
end经过这些年,我每次换行情源或者接入新的交易账户,都会把下面这份清单从头到尾过一遍,在这里分享出来:
| 维度 | 关键问题 |
|---|---|
| 需求匹配 | 策略周期是分钟级还是秒级?是否用到Level-2盘口微观数据? |
| 数据质量 | 是否自带交易所事件时间戳?断线后能否安全恢复全量快照?序列号是否连续可校验? |
| 服务稳定性 | 一周内断线次数多少?重连耗时怎样?有没有超过5秒的推送中断? |
| 工程接入 | WebSocket订阅配额够不够?心跳机制是否健全?回调函数能否独立运行不阻塞? |
| 成本核算 | 订阅费之外的流量费、存储费、历史数据补拉费有没有计算在内? |
| 授权合规 | 行情能否用于实盘?能否二次转发?免费源的授权范围是否清晰? |
| 交易联动 | 行情API和交易API是否同源?订单状态机是否覆盖部分成交、拒单等场景? |
最后聊一点个人体会。以前我选行情源,第一句话问的是“延迟多少”,现在第一句话问的是“断线后多久能恢复、恢复后数据是否完整”。延迟做的是锦上添花,断线恢复才是雪中送炭。一次网络抖动把盘口状态搞坏导致的亏损,远比那几十毫秒延迟省下来的钱多得多。数据源这种东西,接入的时候麻烦一点,后面就顺一点,偷的懒都会在某个交易日加倍还回来。