【量化系统从零构建 #09】复权与清洗:复权因子·字段归一·异常对齐

【量化系统从零构建 #09】复权与清洗:复权因子·字段归一·异常对齐

系列:《量化系统从零构建》|连载项目 · 纯 GET 取数 · 仅依赖 requests
适用:想给落库数据做「复权 + 清洗」的读者;本篇基于 #05 的 daily 表(或合成行)做前复权折算、缺失与异常值剔除、停复牌对齐,不依赖任何行情终端,也不发起网络请求。

1. 你将得到什么

  • 复权 adjust_prices:用复权因子把收盘价折算成前复权价,因子缺失时回退原始价。
  • 清洗 clean:剔除缺失 close、剔除超过涨跌停阈值的异常跳变(疑似停复牌后数据毛刺)。
  • load_daily:从 SQLite 读 daily 表为字典列表,供信号层直接消费。
  • 本篇交付:干净的复权数据,#10/#11 的技术指标就在它之上算。

2. 本篇取数约定

本篇是信号预处理层,不发起网络请求,直接消费 #05 落库的 daily 表(或外部传入的合成行)。复权因子可来自接口/基本面表,本篇把它作为每行已有字段 adj_factor 处理。

3. 复权与清洗思路

步骤函数作用
读取load_dailydaily 表 → 字典列表
复权adjust_pricesclose × 因子 → adj_close(前复权)
清洗clean去 None、去异常涨跌幅

前复权 = 以最新价为基准把历史价折算;本篇用 close × adj_factor 示意(因子口径以你的数据源为准)。后复权换公式即可。

4. 核心模板函数

import sys, sqlite3


def load_daily(conn, code):
    """从 daily 表读出某代码的日线,按日期升序,返回字典列表。"""
    cur = conn.cursor()
    cur.execute(
        "SELECT date,open,high,low,close,volume,amount FROM daily WHERE code=? ORDER BY date",
        (code,))
    cols = ["date", "open", "high", "low", "close", "volume", "amount"]
    return [dict(zip(cols, row)) for row in cur.fetchall()]


def adjust_prices(rows):
    """前复权:adj_close = close * adj_factor;因子缺失则用原始 close。"""
    out = []
    for r in rows:
        f = r.get("adj_factor")
        base = r.get("close")
        ar = dict(r)
        ar["adj_close"] = (base * f) if (f is not None and base is not None) else base
        out.append(ar)
    return out


def clean(rows, max_pct=0.21):
    """清洗:剔除缺失 close,剔除超过涨跌停阈值的异常跳变。返回干净列表。"""
    out, prev = [], None
    for r in rows:
        c = r.get("close")
        if c is None:
            continue
        if prev is not None and prev > 0:
            if abs(c - prev) / prev > max_pct:
                prev = c
                continue
        out.append(r)
        prev = c
    return out


def run_check():
    # 合成日线(含复权因子、一个缺失、一个异常跳变),非真实行情
    rows = [
        {"date": "2024-01-02", "close": 10.0, "adj_factor": 1.0},
        {"date": "2024-01-03", "close": 11.0, "adj_factor": 1.2},
        {"date": "2024-01-04", "close": None, "adj_factor": 1.2},   # 缺失
        {"date": "2024-01-05", "close": 20.0, "adj_factor": 1.2},   # 异常跳变(>21%)
    ]
    adj = adjust_prices(rows)
    assert abs(adj[1]["adj_close"] - 11.0 * 1.2) < 1e-9
    cl = clean(adj, max_pct=0.21)
    dates = [r["date"] for r in cl]
    assert "2024-01-04" not in dates   # 缺失剔除
    assert "2024-01-05" not in dates   # 异常跳变剔除
    assert dates == ["2024-01-02", "2024-01-03"]
    print("校验通过")


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--check":
        run_check()
    else:
        print("本篇基于落库数据计算,不发起网络请求;先用 #08 的 backfill 落库,再 load_daily 即得数据。")

5. 跑通示例

把上面的代码复制到本地,填入你的 token 即可直接运行:它会请求对应接口、拉取真实数据,并输出归一化后的结构化字典(各字段含义见前文各小节)。

6. 坑与注意事项

  1. 复权口径要对齐:前/后复权公式不同,因子来源(接口/基本面表)要统一,别混用。
  2. max_pct 是经验阈值:A股涨跌停约 ±10%(ST ±5%),异常跳变常来自停复牌,阈值按你的数据调。
  3. 剔除不是填充clean 直接丢异常行;若想保留,可改为「标记后插值」,看你下游需求。
  4. 复权后再算指标:技术指标务必在 adj_close 上算,否则分红除权会造成假突破。

7. 小结与下篇预告

本篇把落库日线收拾干净:复权折算 + 缺失/异常剔除,得到可直接算指标的 adj_close。信号层第一块完工。

下一篇计划写 #10《技术指标(上):MA · MACD》:在 adj_close 上纯 Python 实现均线(SMA)与 MACD(EMA/DIF/DEA/柱子),给信号层最基础的两类指标。

8. 免责声明

本文仅演示复权与数据清洗的用法,所有代码示例均以合成数据校验,未含任何真实数据;文中示例数据仅作演示用途,不构成投资建议,亦不承诺收益。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值