【Python 量化取数指南 #14】Python 清洗行情数据:复权停牌对齐,回测不翻车

【Python 量化取数指南 #14】Python 清洗行情数据:复权停牌对齐,回测不翻车

系列:《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. 核心模板(全系列复用 + 清洗函数)

python 复制代码
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. 跑通示例:停牌对齐 + 复权折算

python 复制代码
def clean_kline(bars, factor=None):
    # bars: list[dict{date,close,...}] 已按 _hit_key 解析
    import pandas as pd
    df = pd.DataFrame(bars)
    df["date"] = pd.to_datetime(df["date"])
    df = df.sort_values("date").set_index("date")
    # 停牌对齐:按交易日历向前填充缺失交易日(示例用自然日重采样后 ffill)
    df = df.resample("D").ffill().dropna(subset=["close"])
    # 复权折算:若有复权因子序列 factor(同长),后复权价 = 未复权价 * factor
    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}]  # 缺 01-03(停牌)
    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. 坑与注意事项

  1. 不复权会假跳空:分红送股日价格断崖,均线/收益率全错,回测必做复权。
  2. 因子顺序要对齐 :复权因子必须和 K 线按 date 一一对齐,错位比不复权更糟。
  3. 停牌别用 0 填充:用前向填充(ffill)保留最后成交价,用 0 会引入假信号。
  4. 退市后裁掉:已退市标的尾部无新 K 线,回测区间要裁到退市前,避免 NaN。
  5. 因子来源:若证书无复权因子端点,可用「历史快照反推」或外部因子,别凭空编。
  6. 字段名三套 :close/sp/收盘,用 _hit_key。

6. 常见报错速查

报错 / 现象 原因 处理
NaN 扩散 停牌填 0 改用 ffill
复权更错 因子错位 按 date 对齐再乘
回测区间异常 含退市尾 裁到退市前
KeyError 字段名不符 print(bars[0]) 看真实 key

7. 小结与下一篇预告

小结:清洗三件事------复权(因子对齐相乘)、停牌(ffill 不填 0)、退市(裁尾部);没有因子就显式标「未复权」,别假装复权。

下一篇计划写 #15《Python 做数据源灾备:一家挂了自动切》:把多个来源封装成可切换的取数层,一家挂了自动降级。

8. 免责声明

本文仅演示公开数据接口的用法,所有代码示例均为演示数据,不构成任何投资建议;实际返回字段以接口文档与你的证书权限为准。

相关推荐
Python私教1 小时前
DeepSeek本地部署Ollama+知识库,附3个报错解决
人工智能·llm·deepseek
知几蜗牛1 小时前
工具还在跑,AI为什么还能继续说?看懂多模态异步事件流
人工智能
天天被压力1 小时前
【Python 量化取数指南 #13】Python 把行情落库:sqlite 一键存,回测随用随取
java·人工智能·python
冬奇Lab1 小时前
LLM 自动化测试系列(04):Web UI 自动化——Midscene 的视觉驱动脚本化
人工智能·测试
Python私教1 小时前
Python环境配置:conda+PyCharm+换源,附6个坑
人工智能·python·pycharm
大白话AI1 小时前
揭秘 Auto Mode 安全分类器
人工智能
知几蜗牛1 小时前
模型后训练别一上来就凭感觉打分:奖励顺序比阶段数量更重要
人工智能
IT_陈寒1 小时前
JavaScript的this指向问题又让我加了个班
前端·人工智能·后端
冬奇Lab1 小时前
一天一个开源项目(第228篇):AX —— Google 开源的「Kubernetes for Agents」,用声明式 YAML 编排十亿级 Agent 任务
人工智能·开源·资讯