【Python 量化取数指南 #14】数据清洗与复权对齐实战
摘要:【Python 量化取数指南 #14】数据清洗与复权对齐实战 系列:《Python 量化取数指南》|连载项目 · 纯 GET 取数 · 仅依赖 requests · 清洗用 pandas 数据:由智
系列:《Python 量化取数指南》|连载项目 · 纯 GET 取数 · 仅依赖 requests · 清洗用 pandas
数据:由智兔数服提供。更多接口见 智兔数服技术博客。
1. 你将得到什么
- 一套停牌对齐 + 退市裁剪的可运行清洗代码(pandas)
- 复权因子的应用方法(含公式),以及「没有复权因子时的兜底」
- 一个离线
run_check(),用合成数据验证对齐逻辑
2. 本篇取数约定
- 历史 K 线:
/hz/history/fsjy/{code}.{market}/{lvl}(第 5 篇已拉) - 复权:接口默认未复权;若你的证书含复权因子端点,取因子后按「后复权价 = 未复权价 × 因子」折算
- 请求:
GET https://api.zhituapi.com<path>?token=<你的智兔token> - 清洗目标是让 K 线「连续可回测」:补齐停牌、裁掉退市后、统一复权口径
3. 核心模板(全系列复用 + 清洗函数)
import time, json, requests
BASE = "https://api.zhituapi.com"
TOKEN = "你的智兔token" # 演示证书(免费版)即可起步
def _get(path, params=None, timeout=15, retry=3, backoff=1.5):
params = dict(params or {})
params["token"] = TOKEN
url = BASE + path
last = None
for i in range(retry):
try:
r = requests.get(url, params=params, timeout=timeout)
if r.status_code != 200:
last = f"HTTP {r.status_code} {r.text[:120]}"
time.sleep(backoff * (i + 1)); continue
try:
return r.json(), None
except ValueError:
last = f"非JSON响应: {r.text[:120]}"
return None, last
except requests.RequestException as e:
last = str(e); time.sleep(backoff * (i + 1))
return None, last
def _hit_key(d, *keys, default=None):
if not isinstance(d, dict):
return default
for k in keys:
if k in d and d[k] not in (None, "", []):
return d[k]
return default
def _to_float(x, default=float("nan")):
try:
return float(x)
except (TypeError, ValueError):
return default
4. 跑通示例:停牌对齐 + 复权折算
def clean_kline(bars, factor=None):
import pandas as pd
df = pd.DataFrame(bars)
df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date").set_index("date")
df = df.resample("D").ffill().dropna(subset=["close"])
if factor is not None:
df["close_adj"] = df["close"] * factor
else:
df["close_adj"] = df["close"]
return df
def run_check():
import pandas as pd
bars = [{"date":"2024-01-02","close":10},{"date":"2024-01-04","close":11}]
df = clean_kline(bars, factor=[1.0, 1.05])
print(f" [run_check] 对齐后 {len(df)} 行,后复权尾值 {df['close_adj'].iloc[-1]:.3f}")
if __name__ == "__main__":
data, err = _get("/hz/history/fsjy/000001.SH/d")
if err:
print("K线失败:", err)
else:
bars = [{"date": _hit_key(b,"date","rq"),
"close": _to_float(_hit_key(b,"close","sp"))} for b in (data if isinstance(data,list) else data.get("data") or [])]
df = clean_kline(bars)
print(f" 清洗后 {len(df)} 行,区间 {df.index[0].date()} ~ {df.index[-1].date()}")
run_check()
返回字段说明:清洗后 DataFrame 索引为 date,含 close(未复权)与 close_adj(复权后)。复权因子 factor 需与 K 线同长;无因子时 close_adj=close,回测须显式标注「未复权」。
5. 坑与注意事项
- 不复权会假跳空:分红送股日价格断崖,均线/收益率全错,回测必做复权。
- 因子顺序要对齐:复权因子必须和 K 线按
date一一对齐,错位比不复权更糟。 - 停牌别用 0 填充:用前向填充(ffill)保留最后成交价,用 0 会引入假信号。
- 退市后裁掉:已退市标的尾部无新 K 线,回测区间要裁到退市前,避免 NaN。
- 因子来源:若证书无复权因子端点,可用「历史快照反推」或外部因子,别凭空编。
- 字段名三套:
close/sp/收盘,用_hit_key。
6. 常见报错速查
| 报错 / 现象 | 原因 | 处理 |
|---|---|---|
NaN 扩散 |
停牌填 0 | 改用 ffill |
| 复权更错 | 因子错位 | 按 date 对齐再乘 |
| 回测区间异常 | 含退市尾 | 裁到退市前 |
KeyError |
字段名不符 | print(bars[0]) 看真实 key |
7. 小结与下一篇预告
小结:清洗三件事——复权(因子对齐相乘)、停牌(ffill 不填 0)、退市(裁尾部);没有因子就显式标「未复权」,别假装复权。
下一篇计划写 #15《多数据源灾备与切换实战》:把多个来源封装成可切换的取数层,一家挂了自动降级。
8. 免责声明
本文仅演示公开数据接口的用法,所有代码示例均为演示数据,不构成任何投资建议;实际返回字段以接口文档与你的证书权限为准。数据由 智兔数服 提供,更多接口示例见 技术博客。
免费领取证书 / 查看完整接口文档,可前往 智兔数服官网。
想亲自试一下?免费获取证书