量化挖矿从过滤噪声开始:精准标记美股tick的盘前盘后时段

用户头像sh_****559rtx
2026-06-02 发布

去年我在搭建一套基于tick的短线因子时,回测曲线异常漂亮,一到实盘就出现莫名其妙的最大回撤。逐笔排查后发现问题出在盘前和盘后数据上——因子把盘前稀疏成交造成的异常价差当成了盘中流动性溢价。如果你也在做高频或日内因子,这个坑迟早会遇到。

数据痛点:tick不标注阶段,但策略却必须分而治之

美股行情api的WebSocket流把所有时段的tick平铺推送,没有天然的分段标记。而盘前、盘中、盘后的微观结构差异非常大,具体可以用下表概括。

阶段 美东时间 数据特征
盘前 04:00-09:30 成交稀疏,跳动偏随机
盘中 09:30-16:00 成交密集,价格连续
盘后 16:00-20:00 波动受消息影响明显

如果让策略无差别地吃进全时段数据,因子中就会混入噪声和假结构,对于依赖统计规律的量化模型来说是致命的。

解决方案:客户端打标的两种路径

第一种,利用时间戳直接推算。将每一个tick的时间戳转成美东时间,然后通过简单的区间判断归类。这个方案几乎适用于所有行情api。

from datetime import datetime
import pytz

# US Eastern timezone
et = pytz.timezone('US/Eastern')

def get_session(ts):
    t = datetime.fromtimestamp(ts, et)
    # Determine pre-market
    if t.hour < 9 or (t.hour == 9 and t.minute < 30):
        return "pre"
    # Regular trading
    if t.hour < 16:
        return "regular"
    # After-hours
    return "after"

第二种,如果接口本身返回sessionTypemarketState字段,可以直接拿来用,省去时区转换。不过从我测过的多个数据源来看,很少有完全不用验证的,边界处还需要自己多加留意。

成本与效率优化:从接入层打标

我在实盘环境中,以AllTick实时WebSocket行情作为底层数据源。它的好处是tick里同时带上时间戳和成交信息,我直接在on_message回调里完成时段判断,并输出带标签的数据流。

import websocket
import json
from datetime import datetime
import pytz

# US Eastern timezone
et = pytz.timezone('US/Eastern')

def session(ts):
    t = datetime.fromtimestamp(ts, et)
    if t.hour < 9 or (t.hour == 9 and t.minute < 30):
        return "pre"
    elif t.hour < 16:
        return "regular"
    else:
        return "after"

def on_message(ws, message):
    data = json.loads(message)
    s = session(data["timestamp"])
    print(f"{data['symbol']} | {s} | {data['price']} | {data['volume']}")

# Open WebSocket connection
ws = websocket.WebSocketApp("wss://ws.alltick.co/stock", on_message=on_message)
ws.run_forever()

这样打标后,我后续的因子计算只需加一个WHERE session='regular'的条件,就把盘前盘后的扰动全部隔绝在外。相比之下,如果在因子内部各自写时间判断,不仅代码冗余,还容易因为某个地方漏判而引入偏差。把阶段识别集中处理,相当于用极低的计算成本换来了整个因子体系的信噪比提升。做量化的朋友都明白,干净的数据比花哨的模型更值钱。

7e225916765ba4e53a225123dc96f8ae.jpg

评论