3. 数据准备与清洗:打好地基才能盖高楼
说实话,很多做量化的人,一上来就急着写策略、调参数。我见过太多人,模型跑得飞起,结果一查数据,全是坑。嗯,数据准备这事儿,看着不起眼,但它是整个策略的根基。
我个人习惯,花在数据清洗上的时间,至少占整个项目时间的40%。你想想看,如果持仓盈亏状态都算错了,那后面的处置效应识别还有什么意义?
3.1 获取历史交易数据
我一般从两个渠道拿数据:一是本地数据库(比如MySQL里存好的日线数据),二是直接从交易所API拉。这里我习惯用 pandas-datareader 或者 akshare 这类库。
举个例子,用 akshare 拿A股数据,代码很简单:
import akshare as ak
import pandas as pd
# 获取个股历史行情
df = ak.stock_zh_a_hist(symbol="000001", period="daily",
start_date="20200101", end_date="20231231")
df.columns = ['date', 'open', 'close', 'high', 'low', 'volume',
'amount', 'amplitude', 'pct_change', 'change', 'turnover']
print(df.head())
这里有个坑,我踩过好几次——不同数据源返回的字段名不一样。有的叫 trade_date,有的叫 date。我建议你拿到数据后,第一时间统一字段命名规范。
date,价格字段统一为 open/high/low/close,成交量用 volume。这样后续写策略时,不用反复查字段名。
3.2 数据清洗与预处理
原始数据拿回来,第一件事不是算指标,而是检查数据质量。我总结了三步走:
- 缺失值处理——看看有没有空值。尤其是停牌日,价格和成交量都是NaN。
- 异常值检测——比如某天涨幅突然变成100%,那多半是数据错误。
- 时间序列对齐——确保日期是连续的,没有跳空。
代码实现起来也不复杂:
# 1. 检查缺失值
print(df.isnull().sum())
# 2. 填充或删除缺失值
df = df.dropna(subset=['close']) # 删除收盘价为空的记录
# 3. 确保日期排序
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values('date').reset_index(drop=True)
# 4. 检查日期连续性
date_range = pd.date_range(start=df['date'].min(), end=df['date'].max(), freq='B')
missing_dates = date_range.difference(df['date'])
if len(missing_dates) > 0:
print(f"缺失交易日: {missing_dates[:5]}")
我曾经遇到过一个项目,数据里连续三天都是同一个价格。一开始以为是行情没波动,后来发现是数据源接口出了问题。嗯,从那以后,我每次清洗完数据,都会先画个K线图看一眼。
3.3 计算持仓盈亏状态
这一步是识别处置效应的关键。说白了,我们要知道每一笔持仓,当前是盈利还是亏损。
我习惯用「持仓成本」和「当前价格」来计算浮动盈亏。假设我们有一笔交易记录:
# 模拟持仓数据
positions = pd.DataFrame({
'stock': ['000001', '000002', '000003'],
'buy_date': ['2023-01-10', '2023-02-15', '2023-03-20'],
'buy_price': [12.5, 8.3, 25.0],
'quantity': [1000, 2000, 500],
'current_price': [13.2, 7.9, 26.5] # 假设当前价格
})
# 计算浮动盈亏
positions['pnl'] = (positions['current_price'] - positions['buy_price']) * positions['quantity']
positions['pnl_pct'] = (positions['current_price'] - positions['buy_price']) / positions['buy_price']
print(positions[['stock', 'pnl', 'pnl_pct']])
这里有个细节:持仓成本要不要考虑交易费用? 我个人建议,如果是做高频或者短线,佣金和印花税会影响盈亏判断,最好算进去。如果是做中长线,可以忽略,因为影响不大。
3.4 标记盈利/亏损持仓
有了盈亏数据,标记就很简单了。我一般用布尔值或者分类标签:
# 标记盈利/亏损
positions['is_profit'] = positions['pnl'] > 0
positions['status'] = positions['pnl'].apply(
lambda x: '盈利' if x > 0 else ('亏损' if x < 0 else '持平')
)
print(positions[['stock', 'pnl', 'status']])
你可能会问:为什么还要标记「持平」? 嗯,在实际交易中,持平状态其实很常见。尤其是做网格交易或者套利策略,经常会出现盈亏在零附近波动的情况。我个人习惯把「持平」单独拎出来,因为处置效应研究的主要是盈利和亏损两种状态下的行为差异。
3.5 本章知识体系总览
下面这张图,是我自己画的数据准备流程。你看一眼,就能明白整个链条是怎么串起来的:
这张图里,我特意把「常见问题与避坑」单独列出来了。因为在实际项目中,数据清洗的坑远比策略逻辑多。你想想看,如果分红送股没处理,持仓成本算出来就是错的,那后面的处置效应分析全白搭。
好了,数据准备这块就聊到这儿。记住一句话:数据干净了,策略才能跑得稳。