第 3 章 · 01 object.py 的基类与行情数据类 本节摘要:本节精读 的基类设计(BaseData)与行情数据类(TickData/BarData)。BaseData 是所有数据类的根,只有两个成员:gatewayname(标记数据来源)和 extra(运行时挂载扩展数据)。TickData 字段最丰富——除常规 OHLC 外,还有国内标准的五档买卖盘(共 20 个字段)、涨停跌停、持仓量。最关键的设计是 自动生成 (symbol.exchange)这个全局唯一复合主键,使用者只需提供本地 symbol,框架自动拼接,避免手工出错。读完本节,你理解了 VeighNa 所有数据的"语法基础"。 内容来源:原项目源码 (L17-108),精读并套用体系化模板。
本节摘要:本节精读
vnpy/trader/object.py的基类设计(BaseData)与行情数据类(TickData/BarData)。BaseData 是所有数据类的根,只有两个成员:gateway_name(标记数据来源)和 extra(运行时挂载扩展数据)。TickData 字段最丰富——除常规 OHLC 外,还有国内标准的五档买卖盘(共 20 个字段)、涨停跌停、持仓量。最关键的设计是__post_init__自动生成vt_symbol(symbol.exchange)这个全局唯一复合主键,使用者只需提供本地 symbol,框架自动拼接,避免手工出错。读完本节,你理解了 VeighNa 所有数据的"语法基础"。
内容来源:原项目源码
vnpy/trader/object.py(L17-108),精读并套用体系化模板。
阅读完本节,你应当能够:
vt_symbol = f"{symbol}.{exchange.value}" 的拼接规则。datetime 别名 Datetime 实际就是标准库 datetime。object.py:5-14:
5 from dataclasses import dataclass, field 6 from datetime import datetime as Datetime # 关键:把标准库 datetime 别名为 Datetime 8 from .constant import Direction, Exchange, Interval, Offset, Status, Product, OptionType, OrderType 11 INFO: int = 20 # 日志级别常量(LogData 默认级别) 14 ACTIVE_STATUSES = set([Status.SUBMITTING, Status.NOTTRADED, Status.PARTTRADED]) # "活跃订单"状态集合
两点:
from datetime import datetime as Datetime(第 6 行):所有字段标注的 Datetime 实际就是标准库 datetime.datetime(不是 date/time)。大写别名纯是命名习惯。所以 LogData.__post_init__ 里可直接 Datetime.now()。ACTIVE_STATUSES(第 14 行):把"提交中/未成交/部分成交"三个状态预定义为集合,用于 OrderData.is_active() 判断。集合查询 O(1)。object.py:17-26:
17 @dataclass 18 class BaseData: 24 gateway_name: str 26 extra: dict | None = field(default=None, init=False)
只有两个成员:
gateway_name: str:标记数据来源接口(如 "CTP"、"BITFINEX")。这是必填字段。因为是基类且 dataclass 继承时字段按 MRO 顺序排列,所有子类字段都排在 gateway_name 之后——这就是为什么子类构造时 gateway_name 总是用关键字传参。extra: dict | None:用 field(init=False) 表示不参与 init 参数,默认 None。留给用户运行时挂载扩展数据,不污染 schema。💡 核心心法:
extra字段是"dataclass 扩展性"的优雅设计。金融数据字段多且杂,核心包定义常用字段,用户有特殊需求(比如想给 TickData 加个"信号强度"标注)时,塞进 extra 即可,不必继承新类。注意 dataclass 规则:init=False 的字段必须放在所有 init=True 字段之后。
object.py:30-85(字段分组):
30 @dataclass 31 class TickData(BaseData): 32 symbol: str 33 exchange: Exchange 34 datetime: Datetime # 静态信息 name: str = "" volume: float = 0 # 当日累计成交量 turnover: float = 0 # 当日累计成交额 open_interest: float = 0 # 持仓量(期货特色) last_price: float = 0 # 最新成交价 last_volume: float = 0 limit_up: float = 0 # 涨停价 limit_down: float = 0 # 跌停价 # OHLC open_price: float = 0 high_price: float = 0 low_price: float = 0 pre_close: float = 0 # 五档买卖盘(国内标准) bid_price_1: float = 0 ... bid_price_5: float = 0 ask_price_1: float = 0 ... ask_price_5: float = 0 bid_volume_1: float = 0 ... bid_volume_5: float = 0 ask_volume_1: float = 0 ... ask_volume_5: float = 0 localtime: Datetime | None = None # 网关本地接收时间戳 82 def __post_init__(self) -> None: 84 self.vt_symbol: str = f"{self.symbol}.{self.exchange.value}"
TickData 字段分五大组:
__post_init__(L82-84):
82 def __post_init__(self) -> None: 84 self.vt_symbol: str = f"{self.symbol}.{self.exchange.value}"
vt_symbol = "600000.SSE" 这种"全局限定符号" = 本地 symbol + "." + 交易所枚举值。
💡 核心心法:这是 VeighNa 最核心的命名约定。
vt_前缀代表 "VeighNa Total"(全局)。为什么需要它?因为不同交易所可能有相同的本地 symbol(比如都有叫 "IF2401" 的合约,但一个在 CFFEX 一个在某海外所),光看本地 symbol 无法区分。拼接成vt_symbol = symbol.exchange.value后全局唯一,跨交易所不会冲突。
__post_init__ 是 dataclass 的钩子方法,在 __init__ 自动生成后调用。VeighNa 用它自动维护复合主键——使用者只需提供本地字段(symbol/exchange),框架自动拼 vt_symbol,避免上层手工拼接字符串出错。
object.py:87-108:
87 @dataclass 88 class BarData(BaseData): 93 symbol: str 94 exchange: Exchange 95 datetime: Datetime 97 interval: Interval | None = None # 周期:分钟/小时/日/周/tick volume / turnover / open_interest / open_price / high_price / low_price / close_price 106 def __post_init__(self): 108 self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
标准 OHLCV + 持仓量 + 周期枚举(Interval.MINUTE/HOUR/DAILY/WEEKLY/TICK)。比 TickData 简洁——没有五档盘口(因为 K 线是聚合后的,不再保留逐笔深度)。
interval 的 value 是字符串简写("1m"/"1h"/"d"/"w"/"tick"),用于数据库存储与 UI 显示。第 6 章讲的 BarGenerator 合成的 Bar 就带 interval 字段。
网关收到行情时构造 TickData:
tick = TickData( symbol="rb2401", exchange=Exchange.SHFE, datetime=datetime.now(), last_price=4200, bid_price_1=4199, bid_volume_1=10, ask_price_1=4201, ask_volume_1=8, gateway_name="CTP" ) # __post_init__ 自动生成:tick.vt_symbol == "rb2401.SHFE"
注意 gateway_name 用关键字传参(因为它来自基类,排在字段顺序最前)。构造后 tick.vt_symbol 自动可用,后续所有缓存/订阅/展示都用这个全局键。
vt_ 前缀 = "VeighNa Total",是全书核心命名约定。下一节,我们看交易相关的数据类——OrderData/TradeData/PositionData/AccountData,以及它们各自的 vt_ 主键规则。