做港股开发那段时间,我碰到一个挺尴尬的问题——碎股行情。正常来说,我们拿到的tick数据都是按每手交易来的,但碎股不一样,它成交零散,流动性低,很多接口干脆不推。一开始我没在意,直到回测策略的时候发现数据对不上,才意识到碎股这块被忽略了。
碎股是什么?简单说就是不足一手的买卖盘。港股市场上,有些股票单价高,投资者可能只买几十股,这种成交不会出现在常规的行情推送里。如果做高频或者量化策略,忽略碎股,数据就不完整了。
为什么碎股行情难抓?
我试过几个港股api,发现碎股数据有几个特点:
推送不稳定:不是每个成交都有推送,频率低很多
字段不统一:有的接口单独给碎股字段,有的直接不提供
延迟较高:碎股成交往往比整手交易晚几秒才出来
用轮询方式基本抓不到,因为碎股更新慢但不定时,轮询间隔设短了浪费资源,设长了容易漏。我后来换成WebSocket,总算能稳定收到了。
实际怎么获取碎股行情
以AllTick API为例,它的WebSocket接口会推送完整的tick数据,里面包含了碎股成交记录。关键是要在订阅的时候明确指定需要哪些字段,不然默认只返回整手数据。
import websocket import json def on_message(ws, message): data = json.loads(message) # 碎股成交会单独标识 for trade in data.get("trades", []): if trade.get("is_odd_lot"): # 碎股标识 print(f"碎股成交 {trade['symbol']} 价格:{trade['price']} 数量:{trade['volume']}") else: print(f"整手成交 {trade['symbol']} 价格:{trade['price']} 数量:{trade['volume']}") def on_open(ws): # 订阅时需要开启碎股数据推送 subscribe_msg = { "action": "subscribe", "symbols": ["00700.HK", "09988.HK"], "include_odd_lot": True # 这个参数很关键 } ws.send(json.dumps(subscribe_msg)) ws = websocket.WebSocketApp( "wss://api.alltick.co/ws/stock", on_open=on_open, on_message=on_message ) ws.run_forever()代码里那个include_odd_lot参数是我翻文档才发现的。一开始没加,死活收不到碎股数据,加上之后立马就有了。
处理碎股数据的几个小技巧
拿到碎股数据后,我通常这样处理:
场景 | 处理方式 |
实时展示 | 碎股单独列一行,和整手区分开 |
策略计算 | 把碎股合并到总成交量里,但单独标记 |
历史回测 | 碎股数据存到另一张表,需要时再关联 |
碎股数据量不大,但我会做一层缓存,把最近五分钟的碎股成交暂存起来。因为有些策略需要判断碎股占比,如果碎股突然变多,可能是个信号。
容易忽略的细节
港股api返回的碎股数据里,有个字段容易误解——trade_type。我一开始以为碎股就是类型为“ODD_LOT”,结果发现有些接口用的是“ODD”,有些直接写在备注里。最好先打印一条数据看看结构,别直接写死判断逻辑。
另外碎股的成交价格有时会和整手有偏差。我碰到过碎股比整手贵两档的情况,后来才明白是投资者急着成交愿意出高价。做策略时别把碎股价格和整手混在一起算均价,会偏。
我的看法
碎股行情看似边缘,但在某些场景下挺有用。比如某只股票突然出现大量碎股买入,可能是有散户提前动手,这种细节有时候比整手数据还敏感。港股api里能稳定提供碎股推送的不多,选接口时这块值得留意。
数据完整度决定了策略的可靠性,碎股虽然零碎,但补齐了才安心。