接收最早数据点
headTimestamp() 是 reqHeadTimeStamp() 的结果回调。它只返回一行核心数据:请求编号和最早时间。
官方参考:Head Timestamp
Python API 中的回调方法在 EWrapper 里:
def headTimestamp(self, reqId: int, headTimestamp: str): """接收最早可用历史数据时间。""" print("headTimestamp", reqId, headTimestamp)| 字段 | 中文解释 |
|---|---|
reqId | 请求时传入的编号,用来对应是哪一个合约或哪一种 whatToShow。 |
headTimestamp | 最早可用时间,格式由请求参数 formatDate 决定。 |
formatDate=1 时,TWS 通常返回可读字符串:
HEAD=reqId=3001;name=aapl_trades_rth_string;timestamp=19801212-14:30:00formatDate=2 时,返回 Unix 时间戳风格的数值:
HEAD=reqId=3003;name=aapl_trades_rth_unix;timestamp=345479400这个时间可以理解为“该请求组合下的最早可用历史数据边界”。它不是行情本身,也不包含开高低收和成交量。
AAPL TRADES 的同一组参考里,formatDate=1 和 formatDate=2 都正常返回,且没有业务错误:
HEAD_ROWS=3HEAD=reqId=3001;name=aapl_trades_rth_string;timestamp=19801212-14:30:00HEAD=reqId=3002;name=aapl_trades_all_string;timestamp=19801212-14:30:00HEAD=reqId=3003;name=aapl_trades_rth_unix;timestamp=345479400NON_INFO_ERROR_COUNT=0from datetime import datetime, timezone
def parse_head_timestamp(value, format_date): """按 formatDate 解析 headTimestamp。""" if format_date == 1: return datetime.strptime(value, "%Y%m%d-%H:%M:%S") if format_date == 2: return datetime.fromtimestamp(int(value), tz=timezone.utc) raise ValueError(f"不支持的 formatDate: {format_date}")formatDate=1 返回的字符串没有显式时区。实际使用时,应结合请求合约、TWS 登录地区、交易所时区和后续 K 线返回格式一起处理。
回调后要做什么
Section titled “回调后要做什么”| 步骤 | 说明 |
|---|---|
记录 reqId 和参数 | 知道这个时间对应哪个合约、whatToShow 和 useRTH。 |
解析 headTimestamp | 按 formatDate 分别处理字符串和 Unix 时间戳。 |
调用 cancelHeadTimeStamp(reqId) | 清理该请求,避免批量任务中请求状态混乱。 |
| 写入合约元数据 | 批量回测或补数时可直接读取缓存,减少重复请求。 |
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
没有收到 headTimestamp() | 连接断开、合约错误、权限不足或请求还没完成 | 先检查 error() 回调和连接状态。 |
| 返回时间比预期晚很多 | 合约上市晚、数据类型不支持更早历史、账户权限不足 | 换 whatToShow 或先确认合约详情。 |
不同 whatToShow 起点不同 | 成交、报价、中点价的数据来源不同 | 每种策略用到的数据类型都单独查询。 |
| RTH 和全时段结果不同 | useRTH=1 会按常规交易时段过滤 | 回测和实盘信号保持同一口径。 |
写入策略配置
Section titled “写入策略配置”收到最早时间后,不建议只存在内存里。回测系统可以把它写入合约元数据或本地缓存:
historical_meta = { "symbol": "AAPL", "secType": "STK", "whatToShow": "TRADES", "useRTH": 1, "earliest": "19801212-14:30:00",}下次启动时可以先用缓存决定请求范围,再定期重新查询一次,兼顾速度和准确性。