2、数据准备与预处理:获取转债行情数据、数据清洗与对齐、复权价格处理、缺失值处理
做量化投资,圈里有个共识:数据和策略,七三开。数据占七,策略占三。这话一点不夸张。你策略再牛,数据是脏的,结果就是垃圾进垃圾出。
我个人习惯,拿到一个策略想法,第一件事不是写代码,而是先想清楚:我需要什么数据?数据从哪里来?怎么保证它干净? 这一步想透了,后面能省80%的坑。
今天我们就来聊聊转债轮动策略的数据准备。说白了,就是给策略做一顿好饭——食材得新鲜,洗得干净,切得整齐。
2.1 获取转债行情数据
转债行情数据,主要分两块:日频行情和基础信息。
- 日频行情:开盘价、收盘价、最高价、最低价、成交量、成交额、转股价值、溢价率等。
- 基础信息:转债代码、正股代码、发行规模、剩余规模、到期日、转股价、下修条款等。
数据源怎么选?我建议用聚宽、Tushare、AkShare这些国内平台。它们接口稳定,数据质量也还行。我在项目中遇到过,有些小平台的数据偶尔会断更,尤其是节假日前后,特别坑。
下面是一个获取转债日行情的示例代码(以聚宽为例):
# 获取转债日行情
from jqdatasdk import *
auth('你的账号', '密码')
# 获取所有转债列表
bonds = get_all_securities(['bond_convert'])
# 获取某只转债的日行情
df = get_price('123456.XSHE',
start_date='2023-01-01',
end_date='2023-12-31',
frequency='daily',
fields=['open','close','high','low','volume','money'])
2.2 数据清洗与对齐
数据拿到手,别急着用。先看看有没有脏数据。常见的坑有:
- 停牌数据:转债停牌期间,价格不变,成交量是0。如果不处理,会干扰策略信号。
- 异常值:比如某天收盘价突然涨了50%,但成交量没放大。这很可能是数据录入错误。
- 日期对齐:转债和正股的交易日可能不完全一致。比如正股停牌,转债还在交易。这时候需要做对齐。
我曾经踩过一个坑:某只转债因为正股重组停牌了两个月,但转债还在交易。我没做对齐,直接把正股数据填了前一天的收盘价。结果策略信号完全乱了。后来我学乖了,停牌期间的数据一律标记为NaN,后面再处理。
数据对齐的代码示例:
# 对齐转债和正股数据
import pandas as pd
# 假设df_bond是转债数据,df_stock是正股数据
# 按日期对齐,只保留共同交易日
df_merged = pd.merge(df_bond, df_stock,
on='date',
how='inner',
suffixes=('_bond', '_stock'))
how='inner'还是how='left'?我个人建议用inner,只保留两者都有交易的日期。这样能避免因停牌导致的虚假信号。
2.3 复权价格处理
转债的复权,和股票不太一样。股票复权是为了消除分红送股的影响。转债复权,主要是为了处理转股价调整和强赎。
转股价调整,通常发生在正股分红、送股、配股时。这时候转债的转股价会下调,转股价值会变化。如果不做复权,历史价格就失去了可比性。
我建议的做法是:用后复权。后复权能保持历史价格的一致性,方便做回测。前复权会改变历史价格,容易让人误解。
复权处理的逻辑:
# 转债后复权处理(简化版)
def adjust_convertible_bond_price(df, adjust_factor):
"""
df: 原始行情数据
adjust_factor: 复权因子(通常由数据源提供)
"""
df['close_adj'] = df['close'] * adjust_factor
df['open_adj'] = df['open'] * adjust_factor
df['high_adj'] = df['high'] * adjust_factor
df['low_adj'] = df['low'] * adjust_factor
return df
2.4 缺失值处理
缺失值,是数据预处理里最烦人的事。常见的处理方法有:
- 删除法:直接删掉有缺失的行。简单粗暴,但会损失数据。
- 填充法:用前一天的数值填充(forward fill),或者用均值填充。
- 插值法:用线性插值或多项式插值。适合连续缺失的情况。
我个人习惯,优先用前向填充。为什么?因为转债市场流动性好,停牌时间一般不长。前一天的收盘价,基本能代表当天的合理价格。如果连续缺失超过3天,我会直接删除。
缺失值处理的代码示例:
# 缺失值处理
# 前向填充,最多填充3天
df.fillna(method='ffill', limit=3, inplace=True)
# 如果还有缺失,直接删除
df.dropna(inplace=True)
2.5 知识体系总览
为了让你更直观地理解整个数据准备流程,我画了一张流程图。嗯,这张图我反复改了好几版,希望能帮你把逻辑理清楚。
你看,整个流程其实不复杂。但每一步都马虎不得。数据准备阶段花的时间,往往占整个策略开发时间的40%以上。别嫌烦,这是值得的。
好了,数据准备好了。下一章我们聊聊怎么构建轮动策略的核心逻辑。嗯,到时候见。