数据准备:获取分钟级K线数据、数据清洗与对齐、成交量与价格的预处理

做量化交易的朋友都知道一句话:垃圾进,垃圾出。VWAP算法调优更是如此——你数据没整干净,后面所有回测都是自欺欺人。

我个人习惯,在开始任何策略开发前,先花至少30%的时间在数据准备上。别嫌多,这步省了,后面哭都来不及。

一、获取分钟级K线数据

VWAP算法对数据粒度有要求。日线太粗,tick级又太细(回测跑起来慢得想砸电脑)。分钟级K线是最佳平衡点。

我一般用1分钟或5分钟K线。为什么?你想想看,VWAP的核心是计算每个时间段的成交量加权均价,分钟级数据刚好能捕捉到日内交易节奏,又不会让计算量爆炸。

数据来源方面,我个人偏好直接从交易所API拉取,或者用第三方数据服务商。这里给个简单的获取示例:

import pandas as pd
import requests

def fetch_minute_kline(symbol, start_date, end_date):
    """
    获取分钟级K线数据
    实际使用时请替换为真实API
    """
    # 伪代码示意
    url = f"https://api.example.com/kline?symbol={symbol}&interval=1min&start={start_date}&end={end_date}"
    response = requests.get(url)
    data = response.json()
    
    df = pd.DataFrame(data)
    df.columns = ['timestamp', 'open', 'high', 'low', 'close', 'volume']
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    df.set_index('timestamp', inplace=True)
    return df

# 使用示例
df_1min = fetch_minute_kline('BTCUSDT', '2024-01-01', '2024-01-31')
print(f"获取到 {len(df_1min)} 条1分钟K线数据")
⚠️ 注意:不同交易所的K线时间戳可能不一致。有的用UTC,有的用本地时间。我踩过这个坑——回测结果漂亮得不行,一上线全对不上,查了两天才发现是时区问题。

二、数据清洗与对齐

拿到原始数据后,别急着用。先做三件事:去重、补缺、对齐

2.1 去重与异常值处理

原始数据里经常有重复行。比如网络重连导致同一根K线被推送两次。我习惯用drop_duplicates()直接干掉。

# 去重
df = df[~df.index.duplicated(keep='first')]

# 检查异常值
# 比如成交量突然为0,或者价格跳空太大
df = df[df['volume'] > 0]  # 剔除成交量为0的K线
df = df[df['close'] > 0]   # 剔除价格为0的K线

嗯,这里要注意:有些品种在夜盘或节假日成交量极低,出现连续几根零成交K线很正常。要不要剔除?看你的策略逻辑。如果VWAP计算需要连续时间序列,我建议保留但标记出来,而不是直接删掉。

2.2 缺失值填充

交易所偶尔会漏发K线。比如某根1分钟K线因为网络波动没推送过来。这时候时间序列就断了。

我遇到过最头疼的情况:回测时VWAP曲线突然跳变,查了半天发现是中间缺了一根K线,导致累计成交量计算错误。

# 重采样到固定频率,填充缺失值
df = df.resample('1T').asfreq()  # 1分钟频率

# 前向填充价格,成交量填0
df['close'].fillna(method='ffill', inplace=True)
df['volume'].fillna(0, inplace=True)
df['open'].fillna(method='ffill', inplace=True)
df['high'].fillna(method='ffill', inplace=True)
df['low'].fillna(method='ffill', inplace=True)
💡 小技巧:成交量填0比填平均值更合理。因为缺失的那根K线确实没有成交,而不是有成交但数据丢了。我早期用平均值填充,结果VWAP被严重扭曲。

2.3 多品种时间对齐

如果你同时交易多个品种(比如BTC和ETH),它们的K线时间戳可能不完全对齐。有的交易所整点推送,有的延迟几秒。

我的做法是:统一以某个品种的时间为基准,其他品种做最近邻对齐

# 假设df_btc和df_eth是两个品种的1分钟K线
# 以BTC时间为基准,对齐ETH
df_eth_aligned = df_eth.reindex(df_btc.index, method='nearest')

说白了,就是让所有数据站在同一个时间轴上。否则你算出来的VWAP是错位的,策略信号会乱套。

三、成交量与价格的预处理

数据对齐后,终于到了预处理环节。这一步直接影响VWAP计算的准确性。

3.1 成交量预处理

原始成交量数据有几个常见问题:

  • 单位不统一:有的品种用"手",有的用"张",有的用"币"
  • 复权影响:股票除权除息后,成交量会异常放大或缩小
  • 异常大单:某根K线突然出现天量成交

我一般这样处理:

# 统一单位(假设全部转为"币")
df['volume'] = df['volume'] * contract_multiplier  # 合约乘数

# 剔除异常成交量(比如超过均值3个标准差)
mean_vol = df['volume'].mean()
std_vol = df['volume'].std()
df = df[df['volume'] < mean_vol + 3 * std_vol]

# 或者用中位数绝对偏差(MAD)方法,更稳健
from scipy.stats import median_abs_deviation
mad = median_abs_deviation(df['volume'])
median_vol = df['volume'].median()
df = df[df['volume'] < median_vol + 5 * mad]
🔑 核心要点:VWAP对成交量异常值非常敏感。一根异常K线就能把全天VWAP拉偏好几个点。我建议用MAD方法,比标准差更抗噪。

3.2 价格预处理

价格数据相对干净,但有几个坑要注意:

  • 跳空缺口:开盘直接跳过前收盘价,比如隔夜重大消息
  • 价格反转:最高价低于最低价(数据错误)
  • 涨跌停板:价格封死,成交量萎缩
# 检查价格合理性
assert (df['high'] >= df['low']).all(), "存在最高价低于最低价的错误数据"
assert (df['high'] >= df['close']).all(), "收盘价高于最高价"
assert (df['low'] <= df['close']).all(), "收盘价低于最低价"

# 处理涨跌停:标记出来,但不删除
df['is_limit_up'] = (df['close'] == df['high']) & (df['volume'] < 0.1 * df['volume'].rolling(20).mean())
df['is_limit_down'] = (df['close'] == df['low']) & (df['volume'] < 0.1 * df['volume'].rolling(20).mean())

我曾经在回测中没处理涨跌停,结果VWAP在涨停板附近出现奇怪的拐点。后来发现是因为涨停时成交量极低,但价格没变,导致加权均价被前几根K线带偏。

3.3 计算VWAP所需的中间变量

预处理完,就可以生成VWAP计算需要的中间变量了:

# 计算典型价格 (Typical Price)
df['typical_price'] = (df['high'] + df['low'] + df['close']) / 3

# 计算价格×成交量
df['pv'] = df['typical_price'] * df['volume']

# 计算累计值(用于滚动VWAP)
df['cum_pv'] = df['pv'].cumsum()
df['cum_vol'] = df['volume'].cumsum()

# 计算VWAP
df['vwap'] = df['cum_pv'] / df['cum_vol']

嗯,这里有个细节:VWAP通常从当天开盘开始重新计算。如果你做的是日内策略,记得每天重置累计值。我习惯按日期分组计算:

# 按日期分组计算VWAP
df['date'] = df.index.date
df['vwap_daily'] = df.groupby('date').apply(
    lambda x: (x['pv'].cumsum() / x['volume'].cumsum())
).reset_index(level=0, drop=True)

四、知识体系总览

下面这张图概括了本章的核心流程。我画的时候特意把数据清洗放在中间位置——因为它是承上启下的关键环节。

数据准备流程总览 获取分钟级K线 1分钟 / 5分钟 API拉取 / 数据服务商 数据清洗与对齐 去重 → 补缺 → 对齐 异常值处理 多品种时间对齐 成交量与价格预处理 单位统一 异常大单剔除 涨跌停标记 输出:清洗后的分钟级数据 含 VWAP 中间变量(typical_price, pv, cum_pv, cum_vol) ⚠️ 常见坑点:时区不一致、缺失K线、涨跌停成交量失真 💡 核心原则:垃圾进,垃圾出 — 数据质量决定策略上限 📌 个人经验 我每次跑新策略前,都会先花15分钟检查数据质量。 画个成交量分布图、价格走势图,肉眼扫一遍,能发现很多隐藏问题。

五、避坑指南

最后,分享几个我踩过的坑:

  1. 别信默认数据:有些数据服务商返回的K线已经做过平滑处理。我遇到过"漂亮"的VWAP曲线,结果发现是数据被插值过。
  2. 注意交易时间:数字货币是7×24小时,但股票有开盘收盘。VWAP计算范围要对应交易时段。
  3. 复权处理要谨慎:股票除权后,价格和成交量都会变。我建议用前复权数据,但成交量也要相应调整。
  4. 保存原始数据:预处理后的数据别覆盖原始文件。我习惯加个_clean后缀,方便回溯。
💡 我的工作流:每次数据准备完,我会生成一份数据质量报告,包含缺失率、异常值数量、时间跨度等指标。这样后续调参时,能快速定位问题是不是出在数据上。

好了,数据准备这块就聊到这儿。记住一句话:数据干净了,VWAP才有意义。下一节我们会基于这些数据,开始真正的参数调优。


公众号:蓝海资料掘金营,微信deep3321