4. 市场数据事件:Tick数据与Bar数据的结构定义、数据事件的数据模型、数据源适配器接口设计

在事件驱动回测与灰度发布系统中,市场数据是驱动所有策略逻辑运转的原始燃料。本章将严格定义Tick与Bar两种核心数据结构的字段规范,构建统一的数据事件模型,并设计可插拔的数据源适配器接口,确保系统能够无缝对接不同来源的行情数据。

4.1 Tick数据结构定义

Tick数据是逐笔成交或逐笔报价的原始记录,代表市场在某一瞬间的微观快照。在低延迟回测与高频策略灰度验证中,Tick数据的精度直接影响订单簿重建与滑点模拟的准确性。

字段名 类型 说明 示例
symbol string 交易标的代码 "BTC-USDT"
exchange string 交易所标识 "BINANCE"
timestamp int64 Unix毫秒时间戳 1718000000123
last_price float64 最新成交价 67500.50
last_volume float64 最新成交量(基础币种) 0.023
bid_price float64 买一价 67499.00
bid_volume float64 买一量 1.500
ask_price float64 卖一价 67501.00
ask_volume float64 卖一量 0.800
tick_direction int8 成交方向:1=主动买,-1=主动卖,0=未知 1
设计要点:Tick数据必须包含完整的买卖盘口(Bid/Ask)信息,而非仅成交价。灰度发布中,灰度实例需要根据盘口深度模拟限价单的成交概率,缺少盘口的Tick数据会导致滑点模型失真。

4.2 Bar数据结构定义

Bar数据(K线)是Tick数据在固定时间窗口内的聚合结果。Bar数据在回测中用于降低计算负载,在灰度发布中用于趋势型策略的快速验证。

字段名 类型 说明 示例
symbol string 交易标的代码 "ETH-USDT"
exchange string 交易所标识 "OKX"
timestamp int64 Bar起始时间(毫秒) 1718000100000
open float64 开盘价 3450.00
high float64 最高价 3480.50
low float64 最低价 3440.20
close float64 收盘价 3475.80
volume float64 成交量(基础币种) 12500.000
turnover float64 成交额(计价币种) 43250000.00
count int32 该Bar内的成交笔数 892
灰度发布注意:Bar数据的时间戳必须严格对齐到时间窗口的起始边界。灰度实例与生产实例使用不同时间窗口(如1min vs 5min)时,需在数据源适配器层完成对齐,否则策略信号会产生相位偏差。

4.3 数据事件的数据模型

为了统一处理Tick和Bar两种数据,我们设计一个泛型数据事件模型。该模型将原始数据封装为事件对象,携带元数据(如事件类型、生成时间、数据源标识),便于事件总线进行路由和分发。

// MarketDataEvent 泛型数据事件模型
type MarketDataEvent struct {
    EventID     string      // 全局唯一事件ID
    EventType   EventType   // 事件类型:TickEvent / BarEvent
    SourceID    string      // 数据源适配器ID
    Symbol      string      // 交易标的
    Exchange    string      // 交易所
    EventTime   int64       // 事件生成时间(纳秒)
    ReceiveTime int64       // 事件到达系统时间(纳秒)
    Data        interface{} // 具体数据:*TickData 或 *BarData
    Metadata    map[string]string // 扩展元数据
}

// EventType 事件类型枚举
type EventType int8

const (
    TickEvent EventType = iota + 1
    BarEvent
)
延迟测量:EventTime与ReceiveTime的差值即为数据源到事件总线的传输延迟。在灰度发布中,该延迟用于评估灰度实例与生产实例之间的数据同步质量,延迟超过阈值时应触发告警。

4.4 数据源适配器接口设计

数据源适配器是连接外部行情源与事件驱动引擎的桥梁。我们定义一套标准接口,使得CSV文件、数据库、WebSocket实时流等不同来源的数据都能以统一的方式注入系统。

// DataSourceAdapter 数据源适配器接口
type DataSourceAdapter interface {
    // Init 初始化适配器,传入配置参数
    Init(config map[string]interface{}) error

    // Connect 建立与数据源的连接
    Connect() error

    // Subscribe 订阅指定标的的数据
    Subscribe(symbols []string, exchange string) error

    // Start 启动数据流,将数据事件推送到事件通道
    Start(eventChan chan<- MarketDataEvent) error

    // Stop 停止数据流并释放资源
    Stop() error

    // Status 返回适配器当前状态
    Status() AdapterStatus
}

// AdapterStatus 适配器状态
type AdapterStatus struct {
    Connected     bool
    SubscribedNum int
    LastEventTime int64
    ErrorCount    int
}

接口设计遵循以下原则:

  • 生命周期管理:Init → Connect → Subscribe → Start → Stop,每个阶段可独立重试。
  • 背压感知:Start方法接收带缓冲的eventChan,当通道满时适配器应主动降速或丢弃旧数据。
  • 状态可观测:Status()返回实时状态,供灰度发布控制面监控数据源健康度。

4.5 数据流架构总览

下图展示了从原始数据源到事件驱动引擎的完整数据流,以及适配器层在其中的核心位置。

市场数据事件流架构 CSV文件回放 数据库历史数据 WebSocket实时流 CSV适配器 DB适配器 WS适配器 事件总线 (MarketDataEvent Channel) 事件驱动引擎 (回测/灰度) 适配器层将异构数据源统一为 MarketDataEvent,通过事件总线解耦生产与消费

4.6 适配器实现对比

不同数据源适配器在实现细节上存在显著差异,下表总结了三种典型适配器的关键特性:

特性 CSV适配器 DB适配器 WebSocket适配器
数据速率 可控(按行读取) 受限于查询性能 实时最高速率
时间控制 支持回放加速/暂停 按时间范围查询 实时时间戳
重连机制 无需重连 连接池自动重连 指数退避重连
数据完整性 全量回放 依赖数据库记录 可能丢包需补订
典型延迟 微秒级(内存读取) 毫秒级(网络+IO) 亚毫秒级(本地)
灰度发布最佳实践:灰度实例建议使用CSV适配器进行历史数据回放,以验证策略逻辑的正确性;生产实例使用WebSocket适配器接入实时流。两者通过事件总线隔离,互不影响。

4.7 数据事件路由规则

事件总线根据MarketDataEvent中的元数据将事件路由到对应的策略实例。路由规则支持多级匹配:

  1. 按标的路由:同一策略可能订阅多个标的,事件根据Symbol字段分发。
  2. 按事件类型路由:高频策略订阅Tick事件,趋势策略订阅Bar事件。
  3. 按灰度标签路由:灰度实例携带Metadata["gray_tag"],事件总线根据标签将数据副本发送到灰度通道。
// 事件路由示例:根据灰度标签分发
func (bus *EventBus) Route(event MarketDataEvent) {
    // 生产通道:所有实例
    bus.producerChan <- event

    // 灰度通道:仅当事件携带灰度标签
    if tag, ok := event.Metadata["gray_tag"]; ok {
        if grayChan, exists := bus.grayChannels[tag]; exists {
            select {
            case grayChan <- event:
            default:
                // 灰度通道满时丢弃,避免阻塞生产通道
                bus.metrics.DiscardEvent(tag)
            }
        }
    }
}
关键约束:灰度通道必须使用非阻塞发送(select + default),防止灰度实例处理缓慢时反压生产通道。灰度发布期间,生产通道的稳定性优先级高于灰度通道的完整性。