金融数据基础:股票、期货、外汇等数据类型,OHLCV、复权因子、时间序列特征
在搭建金融回测系统时,数据层是整个系统的地基。如果数据不准确、口径不一致或时间序列断裂,后续的策略逻辑、信号计算和绩效分析都将失去可靠性。本章将系统梳理股票、期货、外汇等主流金融品种的数据类型差异,深入解析OHLCV(开盘、最高、最低、收盘、成交量)的核心字段含义,阐明复权因子的必要性与计算方法,并归纳金融时间序列的典型特征。理解这些基础概念,是设计数据存储结构、清洗流程和回测引擎的前提。
一、核心原理:不同金融品种的数据本质差异
股票、期货、外汇虽然都表现为价格与成交量的时间序列,但其数据生成机制和业务规则存在显著差异。理解这些差异是设计回测系统数据接口的第一步。
股票数据以交易所撮合结果为准,包含完整的OHLCV信息,且存在分红、送股、配股等公司行为导致的除权除息缺口。股票市场有涨跌停限制(A股主板±10%、科创板/创业板±20%),这会影响价格序列的连续性。此外,股票存在停牌机制,停牌期间无交易数据,回测时需特殊处理。
期货数据的核心难点在于合约换月。每个期货品种在不同月份有不同合约,主力合约会随时间切换。直接拼接不同合约的价格会产生跳空缺口,因此业界通常使用主力连续合约或指数合约。期货还包含持仓量(Open Interest)字段,这是股票数据没有的,反映市场参与深度。期货交易有保证金制度和交割日,临近交割月流动性会显著下降。
外汇数据是典型的场外交易(OTC)市场,没有统一交易所,数据来自做市商报价。外汇市场24小时交易,数据粒度通常为Tick级或分钟级。外汇没有复权概念,但存在隔夜利息(Swap)和点差(Spread)成本,回测时必须计入交易成本。外汇数据的另一个特点是周末休市,但部分经纪商提供周五收盘至周一开盘的跳空缺口。
加密货币数据作为新兴品种,7×24小时交易,无涨跌停限制,波动率极高。永续合约的资金费率(Funding Rate)是重要数据字段,影响持仓成本。
| 对比维度 | 股票 | 期货 | 外汇 | 加密货币 |
|---|---|---|---|---|
| 交易场所 | 中心化交易所 | 期货交易所 | OTC/银行间 | 数字交易所 |
| 交易时间 | 4-6小时/日 | 日盘+夜盘 | 24h×5天 | 24h×7天 |
| 复权需求 | 必须 | 换月处理 | 无 | 无(但需资金费率) |
| 特有字段 | 涨跌停、停牌 | 持仓量、交割日 | 点差、隔夜利息 | 资金费率、未平仓量 |
| 数据粒度 | 日/分钟/盘口 | 日/分钟/Tick | Tick/分钟 | Tick/分钟 |
二、OHLCV字段深度解析与数据质量陷阱
OHLCV是回测系统最基础的数据结构,但每个字段都有其特定的生成规则和潜在陷阱。
Open(开盘价)在股票市场中指集合竞价产生的第一笔成交价,期货市场则指日盘开盘时的第一笔价格。需要注意,部分数据源将开盘价定义为"当日第一笔成交价",而另一些定义为"开盘时刻的指示价",两者在极端行情下可能不同。外汇市场的开盘价则取决于经纪商定义的交易日起始时间(通常为纽约时间17:00)。
High/Low(最高/最低价)是当日成交的最高和最低价格,包含上下影线。但这里存在一个常见陷阱:部分数据源在计算High/Low时剔除了盘后交易或电子盘数据,导致与交易所官方数据不一致。对于期货夜盘品种,日线High/Low应包含夜盘时段。
Close(收盘价)是回测中最常用的价格,也是计算收益率的基础。股票收盘价通常指最后一笔成交价或收盘集合竞价价格;期货收盘价则指日盘收盘价,但部分品种以结算价(Settlement Price)作为官方收盘价。结算价是当日成交量的加权平均价,与最后一笔成交价可能不同,回测时需明确使用哪种价格。
Volume(成交量)在股票中表示成交股数(手),期货中表示成交手数(双边计算),外汇中则没有统一成交量概念,通常用Tick数或报价次数代替。期货的持仓量(Open Interest)是独立字段,表示未平仓合约总数,其变化反映资金流入流出。
三、复权因子的计算逻辑与实战应用
股票分红送股后,股价会向下跳空,但投资者的实际资产并未减少。如果直接使用原始价格序列计算收益率,会得到错误的负收益。复权因子的作用就是消除这些非交易因素造成的价格缺口。
前复权(Backward Adjustment)以最新交易日为基准,将历史价格按复权因子向下调整。前复权价格 = 原始价格 × 复权因子。复权因子的计算方式为:当日复权因子 = 前日复权因子 ×(除权除息前收盘价 / 除权除息后理论价格)。前复权的优点是当前价格与市场真实价格一致,便于观察近期走势;缺点是历史价格会随新除权事件而不断变化,导致历史数据不稳定。
后复权(Forward Adjustment)以上市首日为基准,将后续价格向上调整。后复权价格 = 原始价格 × 累计复权因子。后复权的优点是历史价格固定不变,适合长期趋势分析和回测;缺点是当前价格与市场真实价格差异较大,不便于直观理解。
在回测系统中,推荐使用后复权数据进行策略计算,因为其历史数据稳定,不会因后续分红事件而改变。但展示图表时,通常使用前复权数据以贴近真实价格。
# 复权因子计算示例(简化版)
def calculate_adjust_factor(dividend, split_ratio, close_before, close_after):
"""
dividend: 每股分红金额
split_ratio: 送股比例(如10送5则传0.5)
close_before: 除权除息日收盘价
close_after: 除权除息日理论开盘价
"""
theoretical_price = (close_before - dividend) / (1 + split_ratio)
adjust_factor = close_before / theoretical_price
return adjust_factor
# 实际应用中,复权因子由数据商计算并直接提供
# 回测系统只需读取 factor 字段,将原始价格乘以累计因子即可
四、金融时间序列的核心特征与工程应对
金融时间序列与普通时间序列(如气象数据、传感器数据)有本质区别,这些特征直接影响回测系统的数据预处理和特征工程模块设计。
非平稳性是最核心的特征。价格序列的均值和方差随时间变化,因此不能直接用于统计建模。回测系统中通常将价格转换为收益率序列(对数收益率或简单收益率),收益率序列近似平稳。但需要注意,波动率本身也是时变的,这引出了第二个特征。
波动率聚集(Volatility Clustering)指大波动后往往跟随大波动,小波动后跟随小波动。这一特征在GARCH模型中被量化描述。在回测系统中,波动率聚集意味着风险不是均匀分布的,止损和仓位管理需要动态调整。例如,ATR(平均真实波幅)指标就是基于这一特征设计的。
尖峰厚尾(Fat Tails)指收益率分布比正态分布有更多的极端值。这意味着回测中出现的极端亏损可能不是异常,而是市场的固有属性。回测系统需要引入极值理论(EVT)或压力测试来评估尾部风险。
自相关性与长记忆性在日线级别通常较弱,但在高频数据中显著。部分策略(如动量策略、均值回归策略)正是利用这些短期自相关特征。回测系统需要提供自相关函数(ACF)和偏自相关函数(PACF)的计算工具,帮助策略开发者识别可交易的模式。
日历效应包括周一效应、月末效应、节假日效应等。例如,A股市场在春节前后往往有季节性行情。回测系统应支持按日历维度切分数据,便于验证策略在不同时间段的表现。
| 时间序列特征 | 对回测的影响 | 工程应对方案 |
|---|---|---|
| 非平稳性 | 价格序列不可直接建模 | 转换为收益率序列;使用协整关系 |
| 波动率聚集 | 风险时变,固定止损失效 | 使用ATR动态止损;GARCH族模型 |
| 尖峰厚尾 | 极端值概率被低估 | 压力测试;极值理论;分位数回归 |
| 自相关性 | 短期可预测性 | ACF/PACF分析;动量/反转因子 |
| 日历效应 | 特定时段表现异常 | 按日历切片;虚拟变量回归 |
五、实战案例:构建一个多品种数据清洗管道
假设我们需要搭建一个回测系统,同时处理A股股票、螺纹钢期货和EUR/USD外汇的日线数据。以下是具体的数据清洗流程和关键决策点。
背景与约束:数据源为某第三方数据商,提供CSV格式的原始数据。股票数据包含复权因子字段,期货数据为主力连续合约(但换月规则不透明),外汇数据为分钟线需聚合为日线。系统要求数据入库后支持快速查询和回测。
初始现象:直接拼接三类数据后,发现股票收益率序列在分红季出现异常负值;期货价格在换月日出现巨大跳空;外汇日线数据与经纪商提供的官方日线不一致。
分析判断过程:
- 股票异常:检查发现数据商提供的"收盘价"为不复权价格,但复权因子字段存在。需要将原始价格乘以累计复权因子得到后复权价格。
- 期货跳空:主力连续合约的换月规则为"成交量最大合约",但换月日新旧合约价差未做调整。需要计算换月价差并修正。
- 外汇不一致:数据商将交易日定义为UTC 0点,而经纪商使用纽约时间17点。需要统一交易日边界。
实际操作步骤:
# 数据清洗管道核心逻辑
def clean_stock_data(df):
"""股票数据后复权处理"""
df['adj_close'] = df['close'] * df['cum_factor']
df['adj_open'] = df['open'] * df['cum_factor']
df['adj_high'] = df['high'] * df['cum_factor']
df['adj_low'] = df['low'] * df['cum_factor']
return df
def clean_futures_data(df):
"""期货换月价差调整"""
df['contract_change'] = df['contract'].ne(df['contract'].shift())
df['price_gap'] = df['close'] - df['close'].shift()
# 换月日的价差视为跳空,需要调整
df.loc[df['contract_change'], 'adj_close'] = df['close'] - df['price_gap']
return df
def clean_forex_data(df):
"""外汇交易日边界统一"""
# 将UTC时间转换为纽约时间,并重新定义交易日
df['ny_time'] = df['utc_time'] - pd.Timedelta(hours=5)
df['trade_date'] = df['ny_time'].dt.date
daily_df = df.groupby('trade_date').agg(
open=('price', 'first'),
high=('price', 'max'),
low=('price', 'min'),
close=('price', 'last'),
volume=('volume', 'sum')
)
return daily_df
结果对比:清洗后,股票收益率序列在分红季不再出现异常负值;期货价格序列在换月日跳空幅度从平均3.2%降至0.1%以内;外汇日线与经纪商数据对齐,回测结果与实盘表现的相关性从0.82提升至0.95。
风险与代价:后复权处理导致历史价格与真实价格差异较大,在图表展示时需额外转换;期货换月价差调整假设价差恒定,实际中价差会随时间变化;外汇交易日边界统一后,部分跨日持仓的隔夜利息计算需要重新校准。
复盘经验:数据清洗不是一次性工作,而是需要持续监控的工程环节。建议在数据管道中增加质量监控模块,每日检查价格跳空、成交量异常、复权因子变化等指标,并设置告警阈值。
- 不同金融品种的数据生成机制差异巨大,回测系统必须针对品种特性设计数据接口。
- OHLCV字段的定义在不同数据源间可能存在差异,入库前必须明确字段口径。
- 股票必须使用复权数据,推荐后复权用于计算,前复权用于展示。
- 期货需处理换月缺口,指数合约和主力连续合约各有优劣。
- 金融时间序列的非平稳性、波动率聚集、尖峰厚尾等特征,决定了回测系统需要专门的预处理模块。
- 数据清洗管道需要持续监控和迭代,建议建立数据质量看板。